Come si caricano UITableViewCells personalizzati dai file Xib?


293

La domanda è semplice: come si carica personalizzato UITableViewCelldai file Xib? Ciò ti consente di utilizzare Interface Builder per progettare le tue celle. Apparentemente la risposta non è semplice a causa di problemi di gestione della memoria. Questo thread menziona il problema e suggerisce una soluzione, ma è pre-rilascio NDA e manca di codice. Ecco un lungo thread che discute il problema senza fornire una risposta definitiva.

Ecco un po 'di codice che ho usato:

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];
}

Per utilizzare questo codice, crea MyCell.m / .h, una nuova sottoclasse di UITableViewCelle aggiungi IBOutletsper i componenti che desideri. Quindi creare un nuovo file "Vuoto XIB". Apri il file Xib in IB, aggiungi un UITableViewCelloggetto, imposta il suo identificatore su "MyCellIdentifier", imposta la sua classe su MyCell e aggiungi i tuoi componenti. Infine, collega il IBOutletsai componenti. Si noti che non abbiamo impostato il proprietario del file in IB.

Altri metodi raccomandano di impostare il proprietario del file e avvertono di perdite di memoria se Xib non viene caricato tramite una classe di fabbrica aggiuntiva. Ho testato quanto sopra in Strumenti / Perdite e non ho riscontrato perdite di memoria.

Quindi qual è il modo canonico per caricare celle da Xibs? Impostiamo il proprietario del file? Abbiamo bisogno di una fabbrica? In tal caso, com'è il codice della fabbrica? Se ci sono più soluzioni, chiariamo i pro e i contro di ciascuno di essi ...


2
Qualcuno può modificare l'oggetto per porre effettivamente la domanda, ad esempio "Come si caricano UITableViewCells personalizzati dai file Xib?" (Ignora se ciò non è possibile su StackOverflow.)
Steven Fisher,

1
Per iOS 5 e oltre, questa è la soluzione: stackoverflow.com/questions/15591364/… , che è la stessa della soluzione di giuseppe.
Matt Becker,

Nota veloce, più semplice (2013 milieu) risposta qui stackoverflow.com/questions/15378788/... jamihash
Fattie

Risposte:


288

Ecco due metodi che l' autore originale afferma è stato raccomandato da un ingegnere IB .

Vedi il post attuale per maggiori dettagli. Preferisco il metodo n. 2 in quanto sembra più semplice.

Metodo n. 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;
}

Metodo n. 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;
}

Aggiornamento (2014): il metodo n. 2 è ancora valido ma non è più disponibile documentazione. Una volta era nei documenti ufficiali ma ora è stato rimosso a favore degli storyboard.

Ho pubblicato un esempio funzionante su Github:
https://github.com/bentford/NibTableCellExample

modifica per 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
}

1
Per il metodo 1, non dovresti fare qualcosa del tipo "cell = (BDCustomCell *) [[temporaryController.view retain] autorelease];" quindi cell non viene rilasciato quando viene rilasciato il controller temporaneo?
Tod Cunningham,

Hm. La documentazione che parla di # 2 ti dice ancora di impostare il proprietario della cella nel file XIB, su una classe di controller nota. Forse non importa quando si imposta il proprietario durante il caricamento.
Oscar

@OscarGoldman Il proprietario della cella nel file XIB è una classe (cioè il tipo di proprietario.) Il proprietario della cella in loadNibNamed: proprietario: opzioni: è un oggetto del tipo specificato in XIB.
Bentford,

2
L'opzione n. 2 di @CoolDocMan funziona ancora. Il problema è molto probabile con il pennino. Ecco un esempio: github.com/bentford/NibTableCellExample
bentford

2
Perché questo super vecchio codice è classificato così in alto. Stackoverflow fa qualcosa: /
Nico S.

304

La soluzione giusta è questa:

- (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;
}

questo romperà le app iOS5? Non ho mai visto UINib
Adam Waite il

@AdamWaite La registrazione dei file NIB funziona per iOS 5 e versioni successive, quindi non interrompe le app iOS 5. E UINib esiste anche da iOS 4.
Mecki

Per un buon esempio, controllare il repo git riferimento nella risposta superiore qui: stackoverflow.com/questions/18746929/...
netigger

39

Registrati

Dopo iOS 7, questo processo è stato semplificato fino a ( swift 3.0 ):

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

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

( Nota ) Ciò è possibile anche creando le celle nei file .xibo .stroyboard, come celle prototipo. Se è necessario allegare una classe, è possibile selezionare il prototipo di cella e aggiungere la classe corrispondente ( UITableViewCellovviamente deve essere un discendente di ).

dequeue

E in seguito, dequeued utilizzando ( swift 3.0 ):

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

    cell.textLabel?.text = "Hello"

    return cell
}

La differenza è che questo nuovo metodo non solo degrada la cellula, ma crea anche se inesistente (ciò significa che non devi fare if (cell == nil)shenanigans) e la cella è pronta per l'uso proprio come nell'esempio sopra.

( Avviso ) tableView.dequeueReusableCell(withIdentifier:for:)ha il nuovo comportamento, se si chiama l'altro (senza indexPath:) si ottiene il vecchio comportamento, nel quale è necessario verificarlo nile istanziarlo da soli, notare il UITableViewCell?valore restituito.

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

E, naturalmente, il tipo della classe associata della cella è quella che hai definito nel file .xib per la UITableViewCellsottoclasse o, in alternativa, usando l'altro metodo di registro.

Configurazione

Idealmente, le tue celle sono già state configurate in termini di aspetto e posizionamento del contenuto (come etichette e visualizzazioni di immagini) al momento della registrazione e con il cellForRowAtIndexPathmetodo che le devi semplicemente inserire.

Tutti insieme

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

E, naturalmente, tutto è disponibile in ObjC con gli stessi nomi.


Ecco la versione objC:[self.tableView registerNib:[UINib nibWithNibName:@"BlaBlaTableViewCell" bundle:nil] forCellReuseIdentifier:kCellIdentifier];
Zeb,

33

Prese la risposta di Shawn Craver e la ripulì un po '.

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

Realizzo tutte le sottoclassi di BBCell di UITableViewCell e quindi sostituisco lo standard

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

con:

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

16

Ho usato il metodo n. 2 di bentford :

- (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;
}

Funziona, ma fai attenzione alle connessioni al proprietario del file nel tuo file .xib UITableViewCell personalizzato.

Passando la owner:selftua loadNibNameddichiarazione, imposti UITableViewControllercome proprietario del file il tuoUITableViewCell .

Se si trascina e rilascia sul file di intestazione in IB per impostare azioni e punti vendita, li imposterà come proprietario del file per impostazione predefinita.

In loadNibNamed:owner:options, il codice Apple tenterà di impostare le proprietà sul tuo UITableViewController, dal momento che è il proprietario. Ma non hai quelle proprietà definite lì, quindi ricevi un errore sull'essere conforme alla codifica dei valori-chiave :

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

Se invece viene attivato un evento, otterrai 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

Una semplice soluzione consiste nel puntare le connessioni di Interface Builder al UITableViewCellposto del proprietario del file:

  1. Fare clic con il tasto destro su File's Owner per visualizzare l'elenco delle connessioni
  2. Cattura una schermata con Command-Shift-4 (trascina per selezionare l'area da catturare)
  3. x fuori le connessioni dal proprietario del file
  4. Fare clic con il tasto destro su UITableCell nella gerarchia degli oggetti e aggiungere nuovamente le connessioni.

Ho avuto il problema che hai citato, ma come puntare le connessioni a UITableViewCell invece che al proprietario del File? Non capisco i tuoi passi, ad esempio perché è necessario fare uno screenshot? e quando ho fatto clic sul pulsante Aggiungi accanto all'outlet, non succede nulla
xu huanze

@xuhuanze Ho suggerito di fare uno screenshot in modo da avere un registro di ciò a cui il proprietario di File era già connesso. Quindi è possibile ricreare le stesse connessioni. È necessario trascinare e rilasciare per aggiungere le connessioni, non solo un clic.
funroll

Grazie mille, ho avuto il problema "questa classe non è conforme alla codifica del valore chiave per la chiave" e l'ho risolta con il tuo aiuto. Voglio dire agli altri che anche tu dovresti cambiare una classe del tuo UITableViewCell nella tua classe, che usi come classe cellulare personalizzata.
Denis Kutlubaev,

14

Ho deciso di postare dal momento che non mi piace nessuna di queste risposte - le cose possono sempre essere più semplici e questo è di gran lunga il modo più conciso che ho trovato.

1. Costruisci il tuo Xib in Interface Builder come preferisci

  • Impostare il proprietario del file sulla classe NSObject
  • Aggiungi un UITableViewCell e imposta la sua classe su MyTableViewCellSubclass - se il tuo IB si arresta in modo anomalo (accade in Xcode> 4 al momento della stesura di questo documento), basta usare un UIView dell'interfaccia in Xcode 4 se ce l'hai ancora in giro
  • Posiziona le tue sottoview all'interno di questa cella e collega le tue connessioni IBOutlet alla tua @interface in .h o .m (.m è la mia preferenza)

2. Nella sottoclasse UIViewController o UITableViewController

@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. Nella tua classe MyTableViewCellSubclass

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

    return self;
}

9

Se stai utilizzando Interface Builder per creare celle, verifica di aver impostato l'identificatore in Impostazioni. Quindi controlla che sia lo stesso quando chiami dequeueReusableCellWithIdentifier.

Ho dimenticato per sbaglio di impostare alcuni identificatori in un progetto pesante, e il cambio delle prestazioni era come il giorno e la notte.


8

Il caricamento di UITableViewCells da XIB consente di risparmiare un sacco di codice, ma di solito si traduce in un'orribile velocità di scorrimento (in realtà, non è l'XIB ma l'uso eccessivo di UIVview a causare questo).

Ti suggerisco di dare un'occhiata a questo: riferimento di collegamento


6

Ecco il metodo di classe che ho usato per creare celle personalizzate da XIB:

+ (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;
}

Quindi, nella XIB, ho impostato il nome della classe e riutilizzato l'identificatore. Dopodiché, posso semplicemente chiamare quel metodo nel mio controller di visualizzazione anziché in

[[UITableViewCell] alloc] initWithFrame:]

È abbastanza veloce e viene utilizzato in due delle mie applicazioni di spedizione. È più affidabile di chiamare [nib objectAtIndex:0]e, almeno nella mia mente, più affidabile dell'esempio di Stephan Burlot perché sei sicuro di ottenere una vista solo da una XIB del tipo giusto.


5

La soluzione corretta è questa

- (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; 
    }

4

Ricaricare il NIB è costoso. Meglio caricarlo una volta, quindi istanziare gli oggetti quando hai bisogno di una cella. Nota che puoi aggiungere UIImageViews ecc. Al pennino, anche a più celle, usando questo metodo (iOS5 "registerNIB" di Apple consente solo un oggetto di livello superiore - Bug 10580062 "Tabella iOS5 Register RegisterNib: eccessivamente restrittivo"

Quindi il mio codice è sotto - leggi una volta nel NIB (in inizializzazione come ho fatto o in viewDidload - qualunque cosa. Da quel momento in poi, istanzia il pennino in oggetti quindi scegli quello che ti serve. Questo è molto più efficiente del caricamento del pennino ancora ed ancora.

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

Controllare questo - http://eppz.eu/blog/custom-uitableview-cell/ - un modo davvero conveniente usando una piccola classe che finisce una riga nell'implementazione del controller:

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

inserisci qui la descrizione dell'immagine


3

Il modo corretto per farlo è creare un'implementazione della sottoclasse UITableViewCell, un'intestazione e XIB. Nella XIB rimuovi tutte le viste e aggiungi semplicemente una cella di tabella. Impostare la classe come nome della sottoclasse UITableViewCell. Per il proprietario del file, impostalo come nome della classe della sottoclasse UITableViewController. Connetti il ​​proprietario del file alla cella usando l'output tableViewCell.

Nel file di intestazione:

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

Nel file di implementazione:

@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;
}

3

Quello che faccio per questo è dichiarare un IBOutlet UITableViewCell *cellnella tua classe controller. Quindi invoca il NSBundle loadNibNamedmetodo class, che alimenterà ilUITableViewCell la cella dichiarata sopra.

Per lo xib creerò un xib vuoto e aggiungerò l' UITableViewCelloggetto in IB dove può essere configurato secondo necessità. Questa vista viene quindi connessa alla cella IBOutletnella classe controller.

- (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;
}

Aggiunte NSBundle loadNibNamed (accesso ADC)

Articolo di cocoawithlove.com da cui ho preso il concetto (ottieni l'app di esempio per i numeri di telefono)


3
  1. Crea la tua AbcViewCellsottoclasse di classe personalizzata da UITableViewCell(Assicurati che il nome del tuo file di classe e il nome del pennino siano gli stessi)

  2. Creare questo metodo della classe di estensione.

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

    let cell: AbcViewCell = UITableViewCell.fromNib()


2

Prima importa il tuo file di cella personalizzato #import "CustomCell.h"e poi modifica il metodo delegato come indicato di seguito:

- (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

In Swift 4.2 e Xcode 10

Ho tre file di celle XIB

in ViewDidLoad registra i tuoi file XIB in questo modo ...

Questo è il primo approccio

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")

Il secondo approccio registra direttamente i file XIB in cellForRowAt indexPath:

Queste sono le funzioni del mio delegato di 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

Ecco il mio metodo per questo: caricamento di UITableViewCells personalizzati da file XIB ... Ancora un altro metodo

L'idea è quella di creare una sottoclasse SampleCell UITableViewCellcon una IBOutlet UIView *contentproprietà e una proprietà per ogni sottoview personalizzata che è necessario configurare dal codice. Quindi per creare un file SampleCell.xib. In questo file pennino, modificare il proprietario del file in SampleCell. Aggiungi un contenuto UIViewdimensionato per soddisfare le tue esigenze. Aggiungi e configura tutte le visualizzazioni secondarie (etichetta, visualizzazioni immagini, pulsanti, ecc.) Che desideri. Infine, collega la visualizzazione del contenuto e le visualizzazioni secondarie al proprietario del file.


1

Ecco un approccio universale per la registrazione di celle 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)
    }
}

Spiegazione:

  1. Reusableil protocollo genera l'ID cella dal nome della sua classe. Assicuratevi di seguire la convenzione: cell ID == class name == nib name.
  2. UITableViewCellè conforme al Reusableprotocollo.
  3. UITableView l'estensione toglie la differenza nella registrazione delle cellule tramite pennino o classe.

Esempio di utilizzo:

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
}

0

Non so se esiste un modo canonico, ma ecco il mio metodo:

  • Crea un xib per un ViewController
  • Impostare la classe Proprietario file su UIViewController
  • Elimina la vista e aggiungi un UITableViewCell
  • Imposta la classe di UITableViewCell sulla tua classe personalizzata
  • Imposta l'identificatore di UITableViewCell
  • Impostare l'uscita della vista del controller di visualizzazione su UITableViewCell

E usa questo codice:

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

Nel tuo esempio, usando

[nib objectAtIndex:0]

potrebbe rompersi se Apple modifica l'ordine degli articoli nello xib.


Per me, questo si traduce sempre nella creazione di una nuova istanza. dequeue sembra tornare a zero ogni volta.
strano

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;

0

Questa estensione richiede 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
    }
}

Crea un file Xib che contiene solo 1 UITableViewCell personalizzato.

Caricalo.

let cell: BacteriaCell = try NSBundle.mainBundle().loadView("BacteriaCell")

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

}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.