wake-up-neo.com

Wie lädt man benutzerdefinierte UITableViewCells aus Xib-Dateien?

Die Frage ist einfach: Wie lädt man benutzerdefinierte UITableViewCell aus Xib-Dateien? Auf diese Weise können Sie Interface Builder verwenden, um Ihre Zellen zu entwerfen. Die Antwort ist anscheinend aufgrund von Problemen beim Speichermanagement nicht einfach. Dieser Thread erwähnt das Problem und schlägt eine Lösung vor, ist jedoch vor der NDA-Veröffentlichung und hat keinen Code. Hier ist ein langer Thread , der das Thema diskutiert, ohne eine endgültige Antwort zu geben. 

Hier ist ein Code, den ich verwendet habe:

static NSString *CellIdentifier = @"MyCellIdentifier";

MyCell *cell = (MyCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
    NSArray *nib = [[NSBundle mainBundle] loadNibNamed:CellIdentifier owner:self options:nil];
    cell = (MyCell *)[nib objectAtIndex:0];
}

Erstellen Sie zur Verwendung dieses Codes MyCell.m/.h, eine neue Unterklasse von UITableViewCell, und fügen Sie IBOutlets für die gewünschten Komponenten hinzu. Erstellen Sie dann eine neue "Empty XIB" -Datei. Öffnen Sie die Xib-Datei in IB, fügen Sie ein UITableViewCell-Objekt hinzu, setzen Sie den Bezeichner auf "MyCellIdentifier" und die Klasse auf MyCell, und fügen Sie Ihre Komponenten hinzu. Verbinden Sie schließlich die IBOutlets mit den Komponenten. Beachten Sie, dass wir den Eigentümer der Datei in IB nicht festgelegt haben.

Andere Methoden sprechen sich dafür aus, den Besitzer der Datei festzulegen, und warnen vor Speicherverlusten, wenn Xib nicht über eine zusätzliche Factory-Klasse geladen wird. Ich habe das oben unter Instruments/Leaks getestet und sah keine Speicherlecks. 

Was ist der kanonische Weg, Zellen aus Xibs zu laden? Setzen wir den Besitzer der Datei? Brauchen wir eine Fabrik? Wenn ja, wie sieht der Code für die Fabrik aus? Wenn es mehrere Lösungen gibt, lassen Sie uns die Vor- und Nachteile der einzelnen Lösungen klären ...

284
DrGary

Hier sind zwei Methoden, die der Originalautor von einem IB-Ingenieur empfohlen hat .

Siehe den aktuellen Beitrag für weitere Details. Ich bevorzuge Methode Nr. 2, da dies einfacher erscheint.

Methode 1:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"BDCustomCell"];
    if (cell == nil) {
        // Create a temporary UIViewController to instantiate the custom cell.
        UIViewController *temporaryController = [[UIViewController alloc] initWithNibName:@"BDCustomCell" bundle:nil];
        // Grab a pointer to the custom cell.
        cell = (BDCustomCell *)temporaryController.view;
        [[cell retain] autorelease];
        // Release the temporary UIViewController.
        [temporaryController release];
    }

    return cell;
}

Methode 2:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"BDCustomCell"];
    if (cell == nil) {
        // Load the top-level objects from the custom cell XIB.
        NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:@"BDCustomCell" owner:self options:nil];
        // Grab a pointer to the first object (presumably the custom cell, as that's all the XIB should contain).
        cell = [topLevelObjects objectAtIndex:0];
    }

    return cell;
}

Update (2014): Methode Nr. 2 ist noch gültig, es ist jedoch keine Dokumentation mehr vorhanden. Früher befand es sich in den offiziellen Dokumenten , wird aber jetzt zugunsten von Storyboards entfernt.

Ich habe ein funktionierendes Beispiel auf Github veröffentlicht:
https://github.com/bentford/NibTableCellExample

287
bentford

Die richtige Lösung ist folgende:

- (void)viewDidLoad
{
 [super viewDidLoad];
 UINib *nib = [UINib nibWithNibName:@"ItemCell" bundle:nil];
 [[self tableView] registerNib:nib forCellReuseIdentifier:@"ItemCell"];
}

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
   // Create an instance of ItemCell
   PointsItemCell *cell =  [tableView dequeueReusableCellWithIdentifier:@"ItemCell"];

return cell;
}
299
giuseppe

Registrieren

Nach iOS 7 wurde dieser Vorgang auf ( Swift 3.0 ) vereinfacht:

// For registering nib files
tableView.register(UINib(nibName: "MyCell", bundle: Bundle.main), forCellReuseIdentifier: "cell")

// For registering classes
tableView.register(MyCellClass.self, forCellReuseIdentifier: "cell")

( Hinweis ) Dies ist auch durch das Erstellen der Zellen in den .xib- oder .stroyboard-Dateien als Prototypzellen ..__ möglich. Wenn Sie ihnen eine Klasse zuordnen möchten, können Sie den Prototyp der Zelle auswählen und die entsprechende Klasse hinzufügen (muss natürlich ein Nachkomme von UITableViewCell sein).

Dequeue

Und später mit ( Swift 3.0 ) entschlüsselt:

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
    let cell : UITableViewCell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)

    cell.textLabel?.text = "Hello"

    return cell
}

Der Unterschied besteht darin, dass diese neue Methode die Zelle nicht nur in eine Warteschlange stellt, sondern auch erstellt, wenn sie nicht existiert (dh, dass Sie keine if (cell == nil) shenanigans durchführen müssen) und die Zelle wie im obigen Beispiel verwendet werden kann.

( Warning ) tableView.dequeueReusableCell(withIdentifier:for:) hat das neue Verhalten. Wenn Sie das andere aufrufen (ohne indexPath:), erhalten Sie das alte Verhalten, in dem Sie nach nil suchen und es selbst instanzieren müssen. Beachten Sie den Rückgabewert UITableViewCell?.

if let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as? MyCellClass
{
    // Cell be casted properly
    cell.myCustomProperty = true
}
else
{
    // Wrong type? Wrong identifier?
}

Natürlich ist der Typ der zugehörigen Klasse der Zelle der, den Sie in der .xib-Datei für die Unterklasse UITableViewCell definiert haben, oder alternativ die andere Registermethode.

Aufbau

Idealerweise wurden Ihre Zellen bereits zum Zeitpunkt der Registrierung hinsichtlich Erscheinungsbild und Inhaltspositionierung (wie Beschriftungen und Bildansichten) konfiguriert. Bei der cellForRowAtIndexPath-Methode füllen Sie sie einfach aus.

Alle zusammen

class MyCell : UITableViewCell
{
    // Can be either created manually, or loaded from a nib with prototypes
    @IBOutlet weak var labelSomething : UILabel? = nil
}

class MasterViewController: UITableViewController 
{
    var data = ["Hello", "World", "Kinda", "Cliche", "Though"]

    // Register
    override func viewDidLoad()
    {
        super.viewDidLoad()

        tableView.register(MyCell.self, forCellReuseIdentifier: "mycell")
        // or the nib alternative
    }

    override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
    {
        return data.count
    }

    // Dequeue
    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
    {
        let cell = tableView.dequeueReusableCell(withIdentifier: "mycell", for: indexPath) as! MyCell

        cell.labelSomething?.text = data[indexPath.row]

        return cell
    }
}

Und natürlich ist das alles in ObjC mit den gleichen Namen verfügbar.

38
Can

Nahm Shawn Cravers Antwort und räumte ein bisschen auf.

BBCell.h:

#import <UIKit/UIKit.h>

@interface BBCell : UITableViewCell {
}

+ (BBCell *)cellFromNibNamed:(NSString *)nibName;

@end

BBCell.m:

#import "BBCell.h"

@implementation BBCell

+ (BBCell *)cellFromNibNamed:(NSString *)nibName {
    NSArray *nibContents = [[NSBundle mainBundle] loadNibNamed:nibName owner:self options:NULL];
    NSEnumerator *nibEnumerator = [nibContents objectEnumerator];
    BBCell *customCell = nil;
    NSObject* nibItem = nil;
    while ((nibItem = [nibEnumerator nextObject]) != nil) {
        if ([nibItem isKindOfClass:[BBCell class]]) {
            customCell = (BBCell *)nibItem;
            break; // we have a winner
        }
    }
    return customCell;
}

@end

Ich mache alle meine UITableViewCell-Unterklassen von BBCell und ersetze dann den Standard

cell = [[[BBDetailCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"BBDetailCell"] autorelease];

mit:

cell = (BBDetailCell *)[BBDetailCell cellFromNibNamed:@"BBDetailCell"];
33
vilcsak

Ich habe Bentfords Methode # 2 verwendet:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"BDCustomCell"];
    if (cell == nil) {
        // Load the top-level objects from the custom cell XIB.
        NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:@"BDCustomCell" owner:self options:nil];
        // Grab a pointer to the first object (presumably the custom cell, as that's all the XIB should contain).
        cell = [topLevelObjects objectAtIndex:0];
    }

    return cell;
}

Es funktioniert, aber achten Sie auf Verbindungen zu File's Owner in Ihrer benutzerdefinierten UITableViewCell-.xib-Datei.

Durch Übergeben von owner:self in Ihrer loadNibNamed-Anweisung legen Sie die UITableViewController als Dateieigentümer Ihrer UITableViewCell fest.

Wenn Sie in IB die Header-Datei ziehen und ablegen, um Aktionen und Ausgänge festzulegen, werden diese standardmäßig als Eigentümer der Datei festgelegt. 

In loadNibNamed:owner:options versucht Apples Code, Eigenschaften für Ihre UITableViewController festzulegen, da dies der Besitzer ist. Da diese Eigenschaften jedoch nicht definiert sind, wird eine Fehlermeldung angezeigt, dass Schlüsselwertcodierungskompatibel ist:

*** Terminating app due to uncaught exception 'NSUnknownKeyException', reason:     '[<MyUITableViewController 0x6a383b0> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key myLabel.'

Wenn stattdessen ein Ereignis ausgelöst wird, erhalten Sie eine NSInvalidArgumentException:

-[MyUITableViewController switchValueDidChange:]: unrecognized selector sent to instance 0x8e9acd0
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[MyUITableViewController switchValueDidChange:]: unrecognized selector sent to instance 0x8e9acd0'
*** First throw call stack:
(0x1903052 0x15eed0a 0x1904ced 0x1869f00 0x1869ce2 0x1904ec9 0x5885c2 0x58855a 0x62db76 0x62e03f 0x77fa6c 0x24e86d 0x18d7966 0x18d7407 0x183a7c0 0x1839db4 0x1839ccb 0x1f8b879 0x1f8b93e 0x585a9b 0xb904d 0x2c75)
terminate called throwing an exceptionCurrent language:  auto; currently objective-c

Eine einfache Problemumgehung besteht darin, Ihre Interface Builder-Verbindungen auf die UITableViewCell statt auf den Besitzer der Datei zu verweisen:

  1. Klicken Sie mit der rechten Maustaste auf den Besitzer der Datei, um die Liste der Verbindungen anzuzeigen
  2. Machen Sie mit Command-Shift-4 eine Bildschirmaufnahme (ziehen Sie, um den zu erfassenden Bereich auszuwählen)
  3. x Verbindungen aus dem Dateibesitzer herausnehmen
  4. Klicken Sie mit der rechten Maustaste auf die UITableCell in der Objekthierarchie, und fügen Sie die Verbindungen erneut hinzu.
16
funroll

Ich habe mich entschlossen zu posten, da ich keine dieser Antworten mag - Dinge können immer einfacher sein und dies ist bei weitem der prägnanteste Weg, den ich gefunden habe.

1. Baue dein Xib im Interface Builder nach Belieben

  • Legen Sie den Besitzer der Datei auf die Klasse NSObject fest
  • Fügen Sie eine UITableViewCell hinzu, und legen Sie ihre Klasse auf MyTableViewCellSubclass fest. Wenn Ihr IB abstürzt (dies geschieht zum Zeitpunkt des Schreibens in Xcode> 4), verwenden Sie einfach eine UIView der Schnittstelle in Xcode 4, wenn Sie es noch herumliegen
  • Gestalten Sie Ihre Unteransichten in dieser Zelle und verbinden Sie Ihre IBOutlet-Verbindungen mit Ihrem @Interface in der .h oder .m-Datei (.m ist meine Präferenz).

2. In Ihrer UIViewController- oder UITableViewController-Unterklasse

@implementation ViewController

static NSString *cellIdentifier = @"MyCellIdentier";

- (void) viewDidLoad {

    ...
    [self.tableView registerNib:[UINib nibWithNibName:@"MyTableViewCellSubclass" bundle:nil] forCellReuseIdentifier:cellIdentifier];
}

- (UITableViewCell*) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    MyTableViewCellSubclass *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];

    ...

    return cell;
}

3. In Ihrer MyTableViewCellSubclass

- (id) initWithCoder:(NSCoder *)aDecoder {
    if (self = [super initWithCoder:aDecoder]) {
        ...
    }

    return self;
}
13
webstersx

Wenn Sie den Interface Builder zum Erstellen von Zellen verwenden, überprüfen Sie, ob Sie im Inspector den Bezeichner festgelegt haben. Stellen Sie dann sicher, dass dies beim Aufruf von dequeueReusableCellWithIdentifier der Fall ist.

Ich habe versehentlich vergessen, einige Kennungen in einem tischlastigen Projekt festzulegen, und die Leistungsänderung war wie Tag und Nacht.

9
Alex R. Young

Beim Laden von UITableViewCells aus XIBs wird zwar viel Code gespeichert, dies führt jedoch in der Regel zu einer fürchterlichen Bildlaufgeschwindigkeit (eigentlich ist es nicht die XIB, sondern die übermäßige Verwendung von UIViews, die dies verursacht).

Ich schlage vor, Sie schauen sich das an: Linkverweis

8
Can Berk Güder

Hier ist die Klassenmethode, die ich zum Erstellen von benutzerdefinierten Zellen aus XIBs verwendet habe:

+ (CustomCell*) createNewCustomCellFromNib {

    NSArray* nibContents = [[NSBundle mainBundle]
                            loadNibNamed:@"CustomCell" owner:self options:NULL];

    NSEnumerator *nibEnumerator = [nibContents objectEnumerator];
    CustomCell *customCell= nil;
    NSObject* nibItem = nil;

    while ( (nibItem = [nibEnumerator nextObject]) != nil) {

        if ( [nibItem isKindOfClass: [CustomCell class]]) {
            customCell = (CustomCell*) nibItem;

            if ([customCell.reuseIdentifier isEqualToString: @"CustomCell"]) {
                break; // we have a winner
            }
            else
                fuelEntryCell = nil;
        }
    }
    return customCell;
}

Dann stelle ich in der XIB den Klassennamen ein und verwende die Kennung erneut. Danach kann ich diese Methode einfach in meinem View-Controller anstelle von aufrufen

[[UITableViewCell] alloc] initWithFrame:]

Es ist viel schnell genug und wird in zwei meiner Versandanwendungen verwendet. Es ist zuverlässiger als das Aufrufen von [nib objectAtIndex:0] und zumindest meines Erachtens zuverlässiger als das von Stephan Burlot, da Sie sicher sind, dass Sie nur einen Blick auf eine XIB werfen, die der richtige ist.

6
Shawn Craver

Richtige Lösung ist das

- (void)viewDidLoad
{
    [super viewDidLoad];
    [self.tableView registerNib:[UINib nibWithNibName:@"CustomCell" bundle:[NSBundle mainBundle]] forCellReuseIdentifier:@"CustomCell"];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
    UITableViewCell  *cell = [tableView dequeueReusableCellWithIdentifier:@"CustomCell"];
    return cell; 
    }
5
Hamiz Ahmed

Überprüfen Sie diesen - http://eppz.eu/blog/custom-uitableview-cell/ - wirklich bequemen Weg mit einer winzigen Klasse, die in der Controller-Implementierung eine Zeile endet:

-(UITableViewCell*)tableView:(UITableView*) tableView cellForRowAtIndexPath:(NSIndexPath*) indexPath
{
    return [TCItemCell cellForTableView:tableView
                          atIndexPath:indexPath
                      withModelSource:self];
}

enter image description here

4
Geri

Das Nachladen der NIB ist teuer. Laden Sie sie lieber einmal, und instanziieren Sie die Objekte, wenn Sie eine Zelle benötigen. Beachten Sie, dass Sie mit dieser Methode UIImageViews usw. zur Spitze (auch mehrere Zellen) hinzufügen können (Apples "registerNIB" iOS5 lässt nur ein Objekt der obersten Ebene zu - Bug 10580062 "IOS5 tableView registerNib: zu restriktiv)

Mein Code ist also unten - Sie lesen einmal in der NIB (in initialize wie bei mir oder in viewDidload - was auch immer. Von da an instantiieren Sie die Spitze in Objekte und wählen dann die gewünschte aus. Dies ist viel effizienter als das Laden der Spitze über und über.

static UINib *cellNib;

+ (void)initialize
{
    if(self == [ImageManager class]) {
        cellNib = [UINib nibWithNibName:@"ImageManagerCell" bundle:nil];
        assert(cellNib);
    }
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *cellID = @"TheCell";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID];
    if(cell == nil) {
        NSArray *topLevelItems = [cellNib instantiateWithOwner:nil options:nil];
        NSUInteger idx = [topLevelItems indexOfObjectPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop)
                            {
                                UITableViewCell *cell = (UITableViewCell *)obj;
                                return [cell isKindOfClass:[UITableViewCell class]] && [cell.reuseIdentifier isEqualToString:cellID];
                            } ];
        assert(idx != NSNotFound);
        cell = [topLevelItems objectAtIndex:idx];
    }
    cell.textLabel.text = [NSString stringWithFormat:@"Howdie %d", indexPath.row];

    return cell;
}
4
David H

Der richtige Weg ist das Erstellen einer UITableViewCell-Unterklassenimplementierung, eines Headers und einer XIB. Entfernen Sie in der XIB alle Ansichten und fügen Sie einfach eine Tabellenzelle hinzu. Legen Sie die Klasse als Namen der UITableViewCell-Unterklasse fest. Geben Sie für den Dateieigner den Namen der UITableViewController-Unterklasse an. Verbinden Sie den Dateibesitzer über den tableViewCell-Ausgang mit der Zelle.

In der Headerdatei:

UITableViewCell *_tableViewCell;
@property (assign) IBOutlet UITableViewCell *tableViewCell;

In der Implementierungsdatei:

@synthesize tableViewCell = _tableViewCell;

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    static NSString *kCellIdentifier = @"reusableCell";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kCellIdentifier];
    if (cell == nil) {
        [[NSBundle mainBundle] loadNibNamed:kCellIdentifier owner:self options:nil];
        cell = _tableViewCell;
        self.tableViewCell = nil;
    }

    return cell;
}

Was ich dazu mache, ist das Deklarieren eines IBOutlet UITableViewCell *cell in Ihrer Controller-Klasse . Dann rufen Sie die NSBundle loadNibNamed-Klassenmethode auf, die die UITableViewCell der oben deklarierten Zelle zuführt.

Für das xib werde ich ein leeres xib erstellen und das UITableViewCell-Objekt in IB hinzufügen, wo es nach Bedarf eingerichtet werden kann. Diese Ansicht wird dann mit der Zelle IBOutlet in der Controller-Klasse verbunden.

- (UITableViewCell *)tableView:(UITableView *)table
         cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSLog(@"%@ loading RTEditableCell.xib", [self description] );

    static NSString *MyIdentifier = @"editableCellIdentifier";
    cell = [table dequeueReusableCellWithIdentifier:MyIdentifier];

    if(cell == nil) {
        [[NSBundle mainBundle] loadNibNamed:@"RTEditableCell"
                                      owner:self
                                    options:nil];
    }

    return cell;
}

NSBundle-Ergänzungen loadNibNamed (ADC-Login)

cocoawithlove.com Artikel Ich habe das Konzept von (Beispiel-App für Telefonnummern) bezogen

3
Ryan Townshend

Importieren Sie zuerst Ihre benutzerdefinierte Zellendatei #import "CustomCell.h" und ändern Sie dann die Delegatmethode wie folgt:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

static NSString *simpleTableIdentifier = @"CustomCell";

CustomCell *cell = (CustomCell *)[tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil)
{
    NSArray *nib = [[NSBundle mainBundle] loadNibNamed:@"CustomCell" owner:self options:nil];
    cell = [nib objectAtIndex:0];

    [cell setSelectionStyle:UITableViewCellSelectionStyleNone];
}         

     return cell;
}
2
Mohit
  1. Erstellen Sie Ihre eigene angepasste Klasse AbcViewCell Unterklasse aus UITableViewCell (Stellen Sie sicher, dass der Name der Klassendatei und der Name der NIB-Datei identisch sind.)

  2. Erstellen Sie diese Erweiterungsklassenmethode.

    extension UITableViewCell {
        class func fromNib<T : UITableViewCell>() -> T {
            return Bundle.main.loadNibNamed(String(describing: T.self), owner: nil, options: nil)?[0] as! T
        }
    }
    
  3. Benutze es.

    let cell: AbcViewCell = UITableViewCell.fromNib()

2
William Hu

Hier ist ein universeller Ansatz zum Registrieren von Zellen in UITableView:

protocol Reusable {
    static var reuseID: String { get }
}

extension Reusable {
    static var reuseID: String {
        return String(describing: self)
    }
}

extension UITableViewCell: Reusable { }

extension UITableView {

func register<T: UITableViewCell>(cellClass: T.Type = T.self) {
    let bundle = Bundle(for: cellClass.self)
    if bundle.path(forResource: cellClass.reuseID, ofType: "nib") != nil {
        let nib = UINib(nibName: cellClass.reuseID, bundle: bundle)
        register(nib, forCellReuseIdentifier: cellClass.reuseID)
    } else {
        register(cellClass.self, forCellReuseIdentifier: cellClass.reuseID)
    }
}

Erläuterung:

  1. Das Reusable-Protokoll generiert die Zellen-ID aus dem Klassennamen. Stellen Sie sicher, dass Sie der Konvention folgen: cell ID == class name == nib name.
  2. UITableViewCell entspricht dem Reusable-Protokoll.
  3. Die Erweiterung UITableView beseitigt den Unterschied bei der Registrierung von Zellen über Spitze oder Klasse.

Anwendungsbeispiel:

override func viewDidLoad() {
    super.viewDidLoad()
    let tableView = UITableView()
    let cellClasses: [UITableViewCell.Type] = [PostCell.self, ProfileCell.self, CommentCell.self]
    cellClasses.forEach(tableView.register)
}

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: PostCell.self.reuseID) as? PostCell
    ...
    return cell
}
1
Vadim Bulavin

In Swift 4.2 und Xcode 10

Ich habe drei XIB-Zelldateien

registrieren Sie Ihre XIB-Dateien in ViewDidLoad wie folgt ...

Dies ist der erste Ansatz

tableView.register(UINib.init(nibName: "XIBCell", bundle: nil), forCellReuseIdentifier: "cell1")
tableView.register(UINib.init(nibName: "XIBCell2", bundle: nil), forCellReuseIdentifier: "cell2")
//tableView.register(UINib.init(nibName: "XIBCell3", bundle: nil), forCellReuseIdentifier: "cell3")

Zweiter Ansatz: Registrieren Sie XIB-Dateien direkt in cellForRowAt indexPath:

Dies ist meine Delegierungsfunktion für TableView

//MARK: - Tableview delegates
override func numberOfSections(in tableView: UITableView) -> Int {

    return 1
}

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

    return 6
}

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    //This is first approach
    if indexPath.row == 0 {//Load first XIB cell
        let placeCell = tableView.dequeueReusableCell(withIdentifier: "cell1") as! XIBCell
        return placeCell
    //Second approach
    } else if indexPath.row == 5 {//Load XIB cell3
        var cell = tableView.dequeueReusableCell(withIdentifier:"cell3") as? XIBCell3
        if cell == nil{
            let arrNib:Array = Bundle.main.loadNibNamed("XIBCell3",owner: self, options: nil)!
            cell = arrNib.first as? XIBCell3
        }

        //ADD action to XIB cell button
        cell?.btn.tag = indexPath.row//Add tag to button
        cell?.btn.addTarget(self, action: #selector(self.bookbtn1(_:)), for: .touchUpInside);//selector

        return cell!
    //This is first approach
    } else {//Load XIB cell2
        let placeCell = tableView.dequeueReusableCell(withIdentifier: "cell2") as! XIBCell2

        return placeCell
    }

}
1
iOS

Hier ist meine Methode dafür: Benutzerdefinierte UITableViewCells aus XIB-Dateien laden… Noch eine Methode

Die Idee ist, eine SampleCell-Unterklasse der UITableViewCell mit einer IBOutlet UIView *content-Eigenschaft und einer Eigenschaft für jede benutzerdefinierte Untersicht zu erstellen, die Sie aus dem Code konfigurieren müssen. Dann erstellen Sie eine SampleCell.xib-Datei. Ändern Sie in dieser Nib-Datei den Besitzer der Datei in SampleCell. Fügen Sie einen Inhalt UIView hinzu, dessen Größe Ihren Anforderungen entspricht. Fügen Sie alle gewünschten Unteransichten (Beschriftungen, Bildansichten, Schaltflächen usw.) hinzu und konfigurieren Sie sie. Verknüpfen Sie schließlich die Inhaltsansicht und die Unteransichten mit dem Dateieigentümer.

1
MonsieurDart

Ich weiß nicht, ob es einen kanonischen Weg gibt, aber hier ist meine Methode:

  • Erstellen Sie ein Xib für einen ViewController
  • Legen Sie die File Owner-Klasse auf UIViewController fest
  • Löschen Sie die Ansicht und fügen Sie eine UITableViewCell hinzu
  • Legen Sie die Klasse Ihrer UITableViewCell auf Ihre benutzerdefinierte Klasse fest
  • Legen Sie den Bezeichner Ihrer UITableViewCell fest
  • Stellen Sie den Ausgang Ihrer View Controller-Ansicht auf Ihre UITableViewCell ein

Und benutze diesen Code:

MyCustomViewCell *cell = (MyCustomViewCell *)[_tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
  UIViewController* c = [[UIViewController alloc] initWithNibName:CellIdentifier bundle:nil];
  cell = (MyCustomViewCell *)c.view;
  [c release];
}

In Ihrem Beispiel verwenden Sie

[nib objectAtIndex:0]

kann beschädigt werden, wenn Apple die Reihenfolge der Elemente im Xib ändert.

0
Stephan Burlot
 func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

            let cellReuseIdentifier = "collabCell"
            var cell:collabCell! = tableView.dequeueReusableCell(withIdentifier: cellReuseIdentifier) as? collabCell
            if cell == nil {
                tableView.register(UINib(nibName: "collabCell", bundle: nil), forCellReuseIdentifier: cellReuseIdentifier)
                cell = tableView.dequeueReusableCell(withIdentifier: cellReuseIdentifier) as! collabCell!
            }


            return cell

}
0
Hitesh Chauhan

Diese Erweiterung erfordert Xcode7 beta6

extension NSBundle {
    enum LoadViewError: ErrorType {
        case ExpectedXibToExistButGotNil
        case ExpectedXibToContainJustOneButGotDifferentNumberOfObjects
        case XibReturnedWrongType
    }

    func loadView<T>(name: String) throws -> T {
        let topLevelObjects: [AnyObject]! = loadNibNamed(name, owner: self, options: nil)
        if topLevelObjects == nil {
            throw LoadViewError.ExpectedXibToExistButGotNil
        }
        if topLevelObjects.count != 1 {
            throw LoadViewError.ExpectedXibToContainJustOneButGotDifferentNumberOfObjects
        }
        let firstObject: AnyObject! = topLevelObjects.first
        guard let result = firstObject as? T else {
            throw LoadViewError.XibReturnedWrongType
        }
        return result
    }
}

Erstellen Sie eine Xib-Datei, die nur 1 benutzerdefinierte UITableViewCell enthält.

Lade es.

let cell: BacteriaCell = try NSBundle.mainBundle().loadView("BacteriaCell")
0
neoneye
 NSString *CellIdentifier = [NSString stringWithFormat:@"cell %ld %ld",(long)indexPath.row,(long)indexPath.section];


    NewsFeedCell *cell = (NewsFeedCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    cell=nil;

    if (cell == nil)
    {
        NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:@"NewsFeedCell" owner:nil options:nil];

        for(id currentObject in topLevelObjects)
        {
            if([currentObject isKindOfClass:[NewsFeedCell class]])
            {
                cell = (NewsFeedCell *)currentObject;
                break;
            }
        }
}
return cell;
0
2014