2015-09-19 14 views
5

Ecco una cella dinamicaiOS 8 altezza cambiamento di cella dinamica "ora", ri contenuti cella interna

enter image description here

Nota - nell'esempio, il testo non è data-driven. È solo un testo locale alla cella (prendi in considerazione, ad esempio, un testo di aiuto). In fase di runtime, cambia il testo di UILabel da una parola a molte linee, usando un pulsante effettivamente all'interno della cella. iOS ridimensiona opportunamente la cella e la tabella ....

... ma solo quando la cella viene spostata fuori dallo schermo, quindi nuovamente su.

Come avvisare la vista tabella per ricalcolare tutto "ora"?


(Si prega di notare, questa domanda solo nel caso di iOS 8 +, Xcode7 +, AutoLayout per l'altezza delle celle dinamici.)

+0

emm, immagino che in qualche modo si debba attivare l'autolayout quando si assegna un nuovo testo all'etichetta e la magia interna si riposa. O c'è qualcosa di non così semplice? – sage444

+0

Ho aggiunto una spiegazione per cui non dovresti - mai - cambiare il contenuto dalla cella, e ho aggiunto anche la soluzione su come farlo. –

risposta

1

Presumo che tu non stia impostando la proprietà text dello UILabel all'interno di cellForRowAtIndexPath ma piuttosto da qualche altra parte (o eseguendolo in modo asincrono). Se è così, non aggiornerei l'interfaccia utente lì. Piuttosto, aggiornerei il modello eseguendo il backup del tavolo e quindi chiamerei reloadRowsAtIndexPaths. Ciò consentirà nuovamente la chiamata a cellForRowAtIndexPath, ma a differenza del ricaricamento dell'intera tabella, ciò manterrà con grazia lo contentOffset della tableview esattamente dove si trova.

So che tutto ciò sembra inutilmente complicato, ma la linea di fondo è che non si possiede questa vista, la vista tabella lo fa. Deve fare ogni genere di cose sopra e oltre l'aggiornamento della cella. Ad esempio, se la cellula è cresciuta, individua quali cellule scorrono fuori dalla vista e le deseleziona. Se la cella si restringe, scopri quali celle sono state visualizzate come risultato.

È una danza sorprendentemente complessa. Puoi provare a chiamare setNeedsLayout sulla cella, ma non mi aspetterei che funzioni (e anche se lo fosse, è un approccio fragile). La vista tabella è responsabile della gestione delle celle, quindi se davvero dovresti semplicemente aggiornare il modello e ricaricare quella cella.

+0

Capito, ha perfettamente senso. Non puoi * fare in modo che una cella faccia qualcosa per se stessa, davvero. Dopo tutto, è un'entità che può o non può essere sullo schermo, potrebbe essere formata o riformata in qualsiasi momento dai prototipi, ecc. (Re setNeedsLayout, sembra non fare nulla!) – Fattie

+0

In piena divulgazione, ci sono volte che le persone stanno aggiornando una cella direttamente, in modo asincrono. (L'esempio più comune include tutte quelle varie categorie 'UIImageView' per il caricamento asincrono delle immagini.) Ma funzionano solo se l'altezza della cella non cambia. E architettonicamente, è l'approccio sbagliato. È molto meglio aggiornare il modello e quindi chiamare 'reloadRowsAtIndexPaths'. – Rob

1

Hai provato reloadRowsAtIndexPaths invitando l'indice cellulare? dovrebbe animare alla nuova dimensione, se i vincoli sono impostati correttamente.

0

Non conosco il tuo codice, ma hai davvero eseguito le modifiche dell'interfaccia utente sul thread principale. Lo stesso problema è successo a me ed è stato risolto mettendo l'exectuion sul thread principale.

dispatch_async(dispatch_get_main_queue()) {() -> Void in 
     [...] 
    } 
+0

Ciao Philipp, è un buon punto, ma non il mio problema particolare, ho paura! – Fattie

1

Si dovrebbe chiamare self.tableView.reloadData() solo DOPO hai fatto cambiare il testo dell'etichetta della cella.

Forza il tableView per ridisegnare la cella. Questo è quello che è successo quando si scorre, la cella è reused e viene ridisegnata quando ritorna di nuovo.

EDIT:

Se non può o non fare un reloadData sul tableView, è possibile utilizzare:

self.tableView.beginUpdates() 
self.tableView.reloadRowsAtIndexPaths([NSIndexPath(row:0 section:0)] withRowAnimation:UITableViewRowAnimation.Automatic) 
self.tableView.endUpdates() 
+0

In realtà se il contenuto è statico e il tipo di modulo "help", un semplice reloadData() si accenderà rapidamente – Loegic

+0

@JoeBlow, per favore guarda la risposta modificata – Loegic

+0

@JoeBlow, hai provato l'altra soluzione sopra? – Loegic

2

Modifica altezza

Quindi, fondamentalmente, ci sono due modi per fare:

Il primo è quello di ricaricare effettivamente la cella (non la vista tabella). Ricaricando chiamerà nuova heightForRow (non dimenticate di eliminare la cache, se si sta Caching le dimensioni), che restituirà una corretta nuova altezza:

let indexPaths = [NSIndexPath(forRow: ~the rows in question~, inSection: 0)] 
self.table.reloadRowsAtIndexPaths(indexPaths, withRowAnimation: .Automatic) 

(Si noti, tuttavia, che questo comporta spesso ricaricare più di una riga; in particolare se si seleziona/deseleziona, è necessario ricaricare tutte le righe modificate)

Se tuttavia si desidera modificare SOLO la dimensione della cella e il contenuto in sé e non modificare realmente il contenuto dei dati ... quindi per esempio:

  • hai fatto clic su un pulsante e il tuo culo igned nuovo testo locale nella cella alle prese (forse un testo di aiuto):

  • hai modificato solo il LAYOUT della cella.per esempio, è fatto un carattere più grande, o modificato il margine di un blocco di testo in modo che l'altezza di un blocco di testo modificato, così effettivamente l'altezza della cella complessiva cambiato:

In questo caso invece di ricarico, basta chiamare il seguente, che costringe il Tableview fondamentalmente fare tutte le animazioni, e per questo ha bisogno di nuove altezze, in modo che lo richieda:

self.table.beginUpdates() 
self.table.endUpdates() 

la vera soluzione

vedo cosa il tuo problema è Stai provando a cambiare l'altezza della cella dalla cella attuale, ma non ci riuscirai -> e non dovresti. Vedi, la cella è vista e la vista non dovrebbe avere alcuna idea dei suoi dati - la vista è ciò che presenta. Se hai bisogno di modifiche, dovresti informare il tuo controllore di farlo. Per fare ciò, è possibile utilizzare le notifiche, ma preferibilmente i protocolli/delegati.

Così in un primo momento si crea il protocollo in cella, che verrà utilizzato per informare il controllore, che c'è un cambiamento:

protocol MyCellDelegate { 

    func buttonTappedForCell(cell : UITableViewCell) 
} 

Ora, è necessario conformarsi a tale protocollo nel vostro controller della vista che contiene tavolo:

class MyClassWithTableView : MyCellDelegate 

Infine, è necessario dichiarare delegato nella cella:

class MyCell { 
    var delegate : MyCellDelegate 
} 

e assegnarlo nella configurazione della cella, che probabilmente avete in controller della vista:

cell.delegate = self 

Questa è la configurazione di base per tutti i delegati/protocolli davvero, e ora, quando si fa clic sul pulsante, è possibile inoltrare l'azione per il controller:

@IBAction myButtonTouchUpInside() { 

    self.delegate.buttonTappedForCell(self) 
} 

Dopo aver fatto tutto questo, procedere come in parte 1. Vale a dire, sia reloadRowsAtIndexPaths o un beginUpdates/endUpdates coppia come spiegato in precedenza.

Spero che aiuti!

+0

Ho aggiunto la soluzione al tuo altro problema, visto che finalmente ho capito cosa stai cercando di fare :) –

+0

Ciao Jiri! Mi sono reso conto che ciò che hai spiegato sulla differenza tra 'reloadRowsAtIndexPaths' contro una coppia' beginUpdates'/'endUpdates' è una cosa fondamentale. In realtà sono andato avanti e ho coraggiosamente modificato la tua risposta per espandere su questo punto. Vedi se ti piace .... – Fattie

+0

Grazie! :) Sì, ora è più chiaro :) –