Wie lade ich benutzerdefinierte UITableViewCells aus Xib-Dateien?

293

Die Frage ist einfach: Wie lädt man benutzerdefinierte UITableViewCellaus Xib-Dateien? Auf diese Weise können Sie Interface Builder zum Entwerfen Ihrer Zellen verwenden. Die Antwort ist anscheinend aufgrund von Speicherverwaltungsproblemen nicht einfach. Dieser Thread erwähnt das Problem und schlägt eine Lösung vor, ist jedoch vor der NDA-Veröffentlichung und enthält keinen Code. Hier ist ein langer Thread , der das Problem behandelt, 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];
}

Um diesen Code zu verwenden, erstellen Sie MyCell.m / .h, eine neue Unterklasse von UITableViewCellund fügen Sie sie IBOutletsfür die gewünschten Komponenten hinzu. Erstellen Sie dann eine neue "Leere XIB" -Datei. Öffnen Sie die Xib-Datei in IB, fügen Sie ein UITableViewCellObjekt hinzu, setzen Sie den Bezeichner auf "MyCellIdentifier", setzen Sie die Klasse auf MyCell und fügen Sie Ihre Komponenten hinzu. Schließen Sie zum Schluss die IBOutletsan die Komponenten an. Beachten Sie, dass wir den Eigentümer der Datei nicht in IB festgelegt haben.

Andere Methoden empfehlen, den Eigentümer der Datei festzulegen und vor Speicherverlusten zu warnen, wenn die Xib nicht über eine zusätzliche Factory-Klasse geladen wird. Ich habe das Obige unter Instrumente / Lecks getestet und keine Speicherlecks festgestellt.

Wie kann man also kanonisch Zellen aus Xibs laden? Stellen wir den Eigentümer der Datei ein? 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 ...

DrGary
quelle
2
Kann jemand den Betreff bearbeiten, um tatsächlich die Frage zu stellen, z. B. "Wie laden Sie benutzerdefinierte UITableViewCells aus Xib-Dateien?" (Ignorieren Sie, wenn dies beim Stackoverflow nicht möglich ist.)
Steven Fisher
1
Für iOS 5 und höher ist dies die Lösung: stackoverflow.com/questions/15591364/… , die mit der Lösung von giuseppe identisch ist.
Matt Becker
Kurze Anmerkung, einfachere (2013 Milieu) Antwort hier stackoverflow.com/questions/15378788/… jamihash
Fattie

Antworten:

288

Hier sind zwei Methoden, die der ursprüngliche Autor von einem IB-Ingenieur empfohlen hat .

Weitere Informationen finden Sie im aktuellen Beitrag. Ich bevorzuge Methode 2, da es 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 2 ist noch gültig, es gibt jedoch keine Dokumentation mehr dafür. Früher war es in den offiziellen Dokumenten enthalten , jetzt wird es zugunsten von Storyboards entfernt.

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

Bearbeiten für Swift 4.2

override func viewDidLoad() {
    super.viewDidLoad()

    // Do any additional setup after loading the view.
    self.tblContacts.register(UINib(nibName: CellNames.ContactsCell, bundle: nil), forCellReuseIdentifier: MyIdentifier)
}

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

    let cell = tableView.dequeueReusableCell(withIdentifier: MyIdentifier, for: indexPath) as! ContactsCell

    return cell
}
Bentford
quelle
1
Sollten Sie für Methode 1 nicht so etwas wie "cell = (BDCustomCell *) [[temporärController.view behalten] Autorelease];" Wird die Zelle also nicht freigegeben, wenn der temporäre Controller freigegeben wird?
Tod Cunningham
Hm. In der Dokumentation zu Nummer 2 wird weiterhin angegeben, dass der Eigentümer der Zelle in der XIB-Datei auf eine bekannte Controller-Klasse festgelegt werden soll. Vielleicht spielt es keine Rolle, wann Sie den Besitzer beim Laden einstellen.
Oscar
@OscarGoldman Der Eigentümer der Zelle in der XIB-Datei ist eine Klasse (dh der Eigentümertyp). Der Eigentümer der Zelle in loadNibNamed: owner: options: ist ein Objekt des in der XIB angegebenen Typs.
Bentford
2
@CoolDocMan Option 2 funktioniert weiterhin. Problem ist höchstwahrscheinlich mit der Feder. Hier ist ein Beispiel: github.com/bentford/NibTableCellExample
bentford
2
Warum ist dieser super alte Code so hoch eingestuft? Stackoverflow macht etwas: /
Nico S.
304

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;
}
giuseppe
quelle
Wird das iOS5-Apps kaputt machen? Ich habe UINib
Adam Waite
@AdamWaite Das Registrieren von NIB-Dateien funktioniert für iOS 5 und höher, sodass iOS 5-Apps nicht beschädigt werden. Und UINib existiert sogar seit iOS 4.
Mecki
Ein gutes Beispiel finden Sie in dem Git-Repo, auf das in der oberen Antwort hier verwiesen wird
netigger
39

Registrieren

Nach iOS 7 wurde dieser Prozess 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 kann auch erreicht werden, indem die Zellen in den Dateien .xiboder .stroyboardals Prototypzellen erstellt werden. Wenn Sie ihnen eine Klasse hinzufügen müssen, können Sie den Zellenprototyp auswählen und die entsprechende Klasse hinzufügen (muss UITableViewCellnatürlich ein Nachkomme von sein ).

Dequeue

Und später mit ( Swift 3.0 ) aus der Warteschlange entfernt :

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 nicht nur die Zelle aus der Warteschlange entfernt, sondern auch erstellt, wenn sie nicht vorhanden ist (das bedeutet, dass Sie keine if (cell == nil)Spielereien durchführen müssen), und dass die Zelle wie im obigen Beispiel einsatzbereit ist.

( Warnung ) tableView.dequeueReusableCell(withIdentifier:for:)hat das neue Verhalten. Wenn Sie das andere (ohne indexPath:) aufrufen, erhalten Sie das alte Verhalten, bei dem Sie niles selbst überprüfen und instanziieren müssen. Beachten Sie den UITableViewCell?Rückgabewert.

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

Und natürlich ist der Typ der zugeordneten Klasse der Zelle derjenige, den Sie in der .xib-Datei für die UITableViewCellUnterklasse definiert haben, oder alternativ mit der anderen Registermethode.

Aufbau

Im Idealfall wurden Ihre Zellen bereits zum Zeitpunkt der Registrierung und der cellForRowAtIndexPathMethode, mit der Sie sie einfach ausfüllen, hinsichtlich Aussehen und Position des Inhalts (wie Beschriftungen und Bildansichten) konfiguriert .

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 dies alles in ObjC mit den gleichen Namen verfügbar.

Kann
quelle
Hier ist die objC-Version:[self.tableView registerNib:[UINib nibWithNibName:@"BlaBlaTableViewCell" bundle:nil] forCellReuseIdentifier:kCellIdentifier];
Zeb
33

Nahm Shawn Cravers Antwort und räumte sie 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"];
vilcsak
quelle
16

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 in Ihrer benutzerdefinierten UITableViewCell .xib-Datei auf Verbindungen zum Eigentümer der Datei.

Indem Sie owner:selfIhre loadNibNamedErklärung abgeben, legen Sie den UITableViewControllerEigentümer Ihrer Datei fest UITableViewCell.

Wenn Sie in IB in die Header-Datei ziehen und dort ablegen, um Aktionen und Outlets einzurichten, werden diese standardmäßig als Eigentümer der Datei eingerichtet.

In loadNibNamed:owner:optionsversucht der Apple-Code, Eigenschaften für Ihr Gerät festzulegen UITableViewController, da dies der Eigentümer ist. Da diese Eigenschaften dort jedoch nicht definiert sind, wird die Fehlermeldung angezeigt, dass sie mit der Codierung von Schlüsselwerten kompatibel sind :

*** 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 UITableViewCellanstelle des Dateibesitzers auf Folgendes zu verweisen :

  1. Klicken Sie mit der rechten Maustaste auf den Eigentümer der Datei, um die Liste der Verbindungen aufzurufen
  2. Nehmen Sie mit Command-Shift-4 eine Bildschirmaufnahme auf (ziehen Sie, um den zu erfassenden Bereich auszuwählen).
  3. x Verbinden Sie die Verbindungen vom Eigentümer der Datei
  4. Klicken Sie mit der rechten Maustaste auf die UITableCell in der Objekthierarchie und fügen Sie die Verbindungen erneut hinzu.
Funroll
quelle
Ich hatte das von Ihnen erwähnte Problem, aber wie können Sie die Verbindungen zur UITableViewCell anstelle des Dateibesitzers verweisen? Ich verstehe Ihre Schritte nicht, z. B. warum wird ein Screenshot benötigt? und als ich auf die Schaltfläche zum Hinzufügen neben der Steckdose klickte, passierte nichts
xu huanze
@xuhuanze Ich habe vorgeschlagen, einen Screenshot zu machen, damit Sie aufzeichnen können, mit welchen Dingen der Eigentümer von File bereits verbunden war. Dann können Sie dieselben Verbindungen neu erstellen. Sie müssen ziehen und ablegen, um die Verbindungen hinzuzufügen - nicht nur einen Klick.
Funroll
Vielen Dank, ich hatte das Problem "Diese Klasse ist nicht mit der Schlüsselwertcodierung für den Schlüssel kompatibel" und habe es durch Ihre Hilfe gelöst. Ich möchte anderen mitteilen, dass Sie auch eine Klasse Ihrer UITableViewCell in Ihre Klasse ändern sollten, die Sie als benutzerdefinierte Zellenklasse verwenden.
Denis Kutlubaev
14

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

1. Erstellen Sie Ihre Xib in Interface Builder nach Ihren Wünschen

  • Setzen Sie den Eigentümer der Datei auf die Klasse NSObject
  • Fügen Sie eine UITableViewCell hinzu und setzen Sie ihre Klasse auf MyTableViewCellSubclass. Wenn Ihr IB abstürzt (geschieht zum jetzigen Zeitpunkt in Xcode> 4), verwenden Sie einfach eine UIView von do the interface in Xcode 4, wenn Sie sie noch herumliegen haben
  • Layouten Sie Ihre Unteransichten in dieser Zelle und hängen Sie Ihre IBOutlet-Verbindungen an Ihre @ Schnittstelle in .h oder .m an (.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;
}
webstersx
quelle
9

Wenn Sie Interface Builder zum Erstellen von Zellen verwenden, überprüfen Sie, ob Sie die Kennung im Inspektor festgelegt haben. Überprüfen Sie dann, ob es beim Aufrufen von dequeueReusableCellWithIdentifier identisch ist.

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

Alex R. Young
quelle
8

Das Laden von UITableViewCells aus XIBs spart viel Code, führt jedoch normalerweise zu einer schrecklichen Bildlaufgeschwindigkeit (tatsächlich ist es nicht die XIB, sondern die übermäßige Verwendung von UIViews, die dies verursacht).

Ich schlage vor, Sie werfen einen Blick darauf: Link-Referenz

Kann Berk Güder
quelle
6

Hier ist die Klassenmethode, mit der ich benutzerdefinierte Zellen aus XIBs erstellt 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 habe ich in der XIB den Klassennamen festgelegt und die Kennung wiederverwendet. Danach kann ich diese Methode einfach in meinem View Controller anstelle von aufrufen

[[UITableViewCell] alloc] initWithFrame:]

Es ist schnell genug und wird in zwei meiner Versandanwendungen verwendet. Es ist zuverlässiger als das Anrufen [nib objectAtIndex:0]und meiner Meinung nach zumindest zuverlässiger als das Beispiel von Stephan Burlot, da Sie garantiert nur einen Blick aus einem XIB werfen können, der der richtige Typ ist.

Shawn Craver
quelle
5

Die richtige Lösung ist dies

- (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; 
    }
Hamiz Ahmed
quelle
4

Das Nachladen der NIB ist teuer. Laden Sie es besser einmal und instanziieren Sie die Objekte, wenn Sie eine Zelle benötigen. Beachten Sie, dass Sie mit dieser Methode UIImageViews usw. zur Feder hinzufügen können, auch mehrere Zellen (Apples "registerNIB" iOS5 erlaubt nur ein Objekt der obersten Ebene - Fehler 10580062 "iOS5 tableView registerNib: übermäßig restriktiv"

Mein Code ist also unten - Sie lesen die NIB einmal ein (beim Initialisieren wie ich oder in viewDidload - was auch immer. Von da an instanziieren Sie die Feder in Objekte und wählen dann die gewünschte aus. Dies ist viel effizienter als das Laden der Feder ü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;
}
David H.
quelle
4

Überprüfen Sie dies - http://eppz.eu/blog/custom-uitableview-cell/ - wirklich bequem, indem Sie eine winzige Klasse verwenden, die eine Zeile in der Controller-Implementierung endet:

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

Geben Sie hier die Bildbeschreibung ein

Geri Borbás
quelle
3

Der richtige Weg, dies zu tun, besteht darin, eine UITableViewCell-Unterklassenimplementierung, einen Header und XIB zu erstellen. 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. Machen Sie den Dateieigentümer zum Klassennamen der UITableViewController-Unterklasse. Verbinden Sie den Dateieigentümer über den tableViewCell-Ausgang mit der Zelle.

In der Header-Datei:

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;
}
Cameron Lowell Palmer
quelle
3

Was ich dafür tue, ist eine IBOutlet UITableViewCell *cellin Ihrer Controller-Klasse zu deklarieren . Rufen Sie dann die NSBundle loadNibNamedKlassenmethode auf, die die UITableViewCellder oben deklarierten Zelle zuführt.

Für die xib werde ich eine leere xib erstellen und das UITableViewCellObjekt in IB hinzufügen, wo es nach Bedarf eingerichtet werden kann. Diese Ansicht wird dann mit der Zelle IBOutletin 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, aus dem ich das Konzept bezogen habe (Beispiel-App für Telefonnummern herunterladen)

Ryan Townshend
quelle
3
  1. Erstellen Sie Ihre eigene angepasste Klassenunterklasse AbcViewCellaus UITableViewCell(Stellen Sie sicher, dass der Name Ihrer 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()

William Hu
quelle
2

Importieren Sie zuerst Ihre benutzerdefinierte Zellendatei #import "CustomCell.h"und ändern Sie dann die Delegatenmethode 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;
}
Mohit
quelle
2

In Swift 4.2 und Xcode 10

Ich habe drei XIB-Zellendateien

Registrieren Sie in ViewDidLoad Ihre XIB-Dateien 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 sind meine Tableview-Delegatenfunktionen

//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
    }

}
iOS
quelle
1

Hier ist meine Methode dafür: Laden von benutzerdefinierten UITableViewCells aus XIB-Dateien… Noch eine andere Methode

Die Idee ist, eine SampleCell-Unterklasse von UITableViewCellmit einer IBOutlet UIView *contentEigenschaft und einer Eigenschaft für jede benutzerdefinierte Unteransicht zu erstellen, die Sie aus dem Code konfigurieren müssen. Dann erstellen Sie eine SampleCell.xib-Datei. Ändern Sie in dieser NIB-Datei den Dateieigentümer in SampleCell. Fügen Sie einen Inhalt hinzu, dessen UIViewGröße Ihren Anforderungen entspricht. Fügen Sie alle gewünschten Unteransichten (Beschriftung, Bildansichten, Schaltflächen usw.) hinzu und konfigurieren Sie sie. Verknüpfen Sie abschließend die Inhaltsansicht und die Unteransichten mit dem Dateieigentümer.

MonsieurDart
quelle
1

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. ReusableDas Protokoll generiert eine Zellen-ID aus seinem Klassennamen. Stellen Sie sicher, dass Sie die Konvention befolgen : cell ID == class name == nib name.
  2. UITableViewCellentspricht dem ReusableProtokoll.
  3. UITableView Die Erweiterung abstrahiert den Unterschied bei der Registrierung von Zellen über Feder 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
}
Vadim Bulavin
quelle
0

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

  • Erstellen Sie eine xib für einen ViewController
  • Setzen Sie die File Owner-Klasse auf UIViewController
  • Löschen Sie die Ansicht und fügen Sie eine UITableViewCell hinzu
  • Setzen Sie die Klasse Ihrer UITableViewCell auf Ihre benutzerdefinierte Klasse
  • Legen Sie die Kennung 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 mit

[nib objectAtIndex:0]

kann brechen, wenn Apple die Reihenfolge der Elemente in der xib ändert.

Stephan Burlot
quelle
Für mich führt dies dazu, dass immer eine neue Instanz erstellt wird. Die Warteschlange scheint jedes Mal gleich Null zu sein.
seltsam
0
 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;
2014
quelle
0

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")
neoneye
quelle
0
 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

}
Hitesh Chauhan
quelle