2016-03-14 9 views
6

Ho letto this question e pensare che ho capito la differenza tra i due metodi finché non trovo una strana esempio:dequeueReusableCellWithIdentifier: forIndexPath: VS dequeueReusableCellWithIdentifier:

Set stile tabella vista della cellula sia base, Identifier essere cellulare in Storyboard, codice come di seguito:

import UIKit 

class TableViewController: UITableViewController { 
    var items: [String]! 

    override func viewDidLoad() { 
     super.viewDidLoad() 
     items = ["first", "second", "third"] 
    } 

    override func numberOfSectionsInTableView(tableView: UITableView) -> Int { 
     return 1 
    } 

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

    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { 
     // either works fine 
     let cell = tableView.dequeueReusableCellWithIdentifier("Cell")! // let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) 

     cell.textLabel?.text = items[indexPath.row] 
     return cell 
    } 
} 

enter image description here

Molto semplice, ma quando cambio il metodo tableView:cellForRowAtIndexPath: a 1, 2, 3, 4 casi rispettivamente:

Caso 1:

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { 
    var cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) 
    cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) 

    cell.textLabel?.text = items[indexPath.row] 
    return cell 
} 

Caso 2:

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { 
    var cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) 
    cell = tableView.dequeueReusableCellWithIdentifier("Cell")! 

    cell.textLabel?.text = items[indexPath.row] 
    return cell 
} 

Caso 3:

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { 
    var cell = tableView.dequeueReusableCellWithIdentifier("Cell")! 
    cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) 

    cell.textLabel?.text = items[indexPath.row] 
    return cell 
} 

caso 4:

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { 
    var cell = tableView.dequeueReusableCellWithIdentifier("Cell")! 
    cell = tableView.dequeueReusableCellWithIdentifier("Cell")! 

    cell.textLabel?.text = items[indexPath.row] 
    return cell 
} 

Caso 1, 2 (non funziona):

enter image description here

Caso 3, 4 (funziona bene):

enter image description here

Come spiegare? Penso che aiuti davvero a capire questi due metodi da un'altra prospettiva, qualsiasi opinione è gradita.

risposta

6

In ogni caso, tu a re dequeue due celle per ogni riga. Nei casi 1 e 2, si chiama prima la versione ("Cell", forIndexPath: indexPath).In questo caso la vista tabella termina con due celle per ogni riga, una completamente sovrapposta e oscurando l'altra. Si può vedere questo nella finestra di ispezione vista dato che è possibile modificare l'angolo di visualizzazione per vedere dietro:

enter image description here

(ho modificato il codice cellForRowAtIndexPath come questo:

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { 
    var cell = tableView.dequeueReusableCellWithIdentifier("plainCell", forIndexPath: indexPath) 
    cell.textLabel!.text = "First cell for row \(indexPath.row)" 
    cell = tableView.dequeueReusableCellWithIdentifier("plainCell", forIndexPath: indexPath) 
    cell.textLabel!.text = "Second cell for row \(indexPath.row)" 
    print("Cell being returned is \(cell)") 
    return cell 
} 

a determinati diverse etichette di testo in ciascuna cella.) Nei casi 3 e 4, dove si chiama prima la versione ("Cell"), la vista tabella ha solo una cella per ogni riga.

Perché il diverso comportamento? Se crei una sottoclasse personalizzata di UITableViewCell e la usi nello storyboard, puoi quindi sovrascrivere vari metodi e aggiungere le dichiarazioni print() per vedere cosa sta succedendo. In particolare, awakeFromNib, didMoveToSuperView e deinit. Ciò che traspare è che nei casi 1 e 2, la prima cella viene creata (awakeFromNib) e immediatamente aggiunta (didMoveToSuperView) a una superview, presumibilmente la vista tabella o una delle sue sottoview. Nei casi 3 e 4, la prima cella viene creata ma non viene aggiunta a una superview. Invece qualche tempo dopo, la cellula viene deallocata (deinit).

(Si noti che se la seconda cella è eliminato dalla coda utilizzando la versione ("Cell", forIndexPath: indexPath), anch'essa viene aggiunto immediatamente a una superview. Tuttavia, se la seconda cella viene eliminato dalla coda utilizzando la versione ("Cell"), viene aggiunto solo a una superview dopo la cellForRowAtIndexPath metodo è tornato.)

Quindi la differenza fondamentale è che i risultati ("Cell", forIndexPath: indexPath) versione nella cella viene aggiunto immediatamente alla vista tavolo, prima che anche il cellForRowAtIndexPath ha completato. Questo è suggerito nella domanda/risposta a cui ci si riferisce, poiché indica che la cella dequalificata sarà correttamente dimensionata.

Una volta aggiunto al superview, la prima cella non può essere deallocata poiché vi è ancora un forte riferimento ad essa dalla sua superview. Se le celle vengono rimosse dalla coda con la versione ("Cell"), non vengono aggiunte alla superview, di conseguenza non vi è alcun riferimento sicuro dopo che la variabile cell è stata riassegnata e pertanto vengono deallocate.

Spero che tutto ciò abbia un senso.

+0

La tua risposta è fantastica, grazie mille! – fujianjin6471

1

dequeueReusableCellWithIdentifier: non ti dà garanzie: le cellule potrebbero essere nil, quindi bisogna controllare se il vostro cellulare è nil e gestirlo correttamente o la vostra applicazione andrà in crash.

dequeueReusableCellWithIdentifier:forIndexPath:, d'altra parte, controlla questo per voi (restituisce sempre una cella).

Per il tuo caso particolare (Swift), questo significa che puoi forzatamente forzare-scartare la cella con dequeueReusableCellWithIdentifier:forIndexPath:, mentre dovrai usare la sintassi if let con la seconda.

codici di esempio (in Objective-C, io non uso Swift)

dequeueReusableCellWithIdentifier: forIndexPath:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" atIndexPath:indexPath]; 

    // Here we know the cell is not nil (....atIndexPath: ensures it) 
    cell.textLabel.text = items[indexPath.row]; 

    return cell; 
} 

dequeueReusableCellWithIdentifier:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell"]; 

    // You asked for a cell, but you don't know if it is nil or not 
    // In Swift, here the cell should be a conditional 

    // First, check if the cell is nil 
    if (cell == nil) { 
     // Cell is nil. To avoid crashes, we instantiate an actual cell 
     // With Swift conditional should be something similar 
     cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"Cell"]; 
    } 

    // Here you're sure the cell is not nil 
    // If condicional, you probably will write cell?.textLabel?.text = items[indexPath.row]; 
    cell.textLabel.text = items[indexPath.row]; 

    // Finally, you return the cell which you're 100% sure it's not nil 
    return cell; 
} 
+0

Perché il caso 1, 2 non funziona come previsto? – fujianjin6471

+0

Non sono sicuro (non mi piace Swift, quindi non ho scavato troppo in esso), ma sembra che l'attributo 'textLabel' non sia un condizionale ma un vero' UITextField', quindi provando a scartarlo ('?') fallirà, in quanto è un oggetto reale e non un involucro. Puoi vedere che la cella effettiva ** è lì **, perché altrimenti la tua app si arresterebbe in modo anomalo (restituendo 'nil' in' tableView: cellForRowAtIndexPath: 'solleva un'eccezione e si blocca quando restituisce' nil'). Prova 'cell.textLabel.text' senza'? 'O una miscela di condizionale e forza unwrapping ('? 'E'! '). –

+0

Grazie, ma penso che ciò che ha senso non sia il linguaggio che usiamo. @ La risposta di pbasdf mi ha illuminato, aiuta davvero – fujianjin6471