Per sapere quando una vista tabella termina il caricamento del suo contenuto, dobbiamo prima avere una conoscenza di base di come le viste vengono visualizzate sullo schermo.
Nel ciclo di vita di un'app, ci sono 4 momenti chiave:
- L'app riceve un evento (touch, timer, blocco spedito ecc.)
- L'app gestisce l'evento (modifica un vincolo, avvia un'animazione, cambia sfondo ecc.)
- L'app calcola la nuova gerarchia di viste
- L'app esegue il rendering della gerarchia di visualizzazione e la visualizza
I tempi 2 e 3 sono totalmente separati. Perché ? Per motivi di prestazioni, non vogliamo eseguire tutti i calcoli del momento 3 ogni volta che viene apportata una modifica.
Quindi, penso che stai affrontando un caso come questo:
tableView.reloadData()
tableView.visibleCells.count // wrong count oO
Cosa c'è che non va qui?
Come ogni vista, una vista tabella ricarica pigramente il suo contenuto. In realtà, se chiami reloadDatapiù volte non creerà problemi di prestazioni. La vista tabella ricalcola solo le dimensioni del contenuto in base all'implementazione del delegato e attende il momento 3 per caricare le celle. Questa volta si chiama passaggio layout.
Ok, come accedere al layout layout?
Durante il passaggio del layout, l'app calcola tutti i frame della gerarchia della vista. Per partecipare, è possibile ignorare i metodi dedicati layoutSubviews, updateLayoutConstraintsecc. In UIViewae i metodi equivalenti in una sottoclasse del controller di visualizzazione.
Questo è esattamente ciò che fa una vista tabella. Sostituisce layoutSubviewse in base all'implementazione del delegato aggiunge o rimuove le celle. Chiama cellForRowsubito prima di aggiungere e creare una nuova cella, willDisplaysubito dopo. Se hai chiamato reloadDatao hai appena aggiunto la vista tabella alla gerarchia, la vista tabelle aggiunge tutte le celle necessarie per riempire il suo frame in questo momento chiave.
Bene, ma ora, come sapere quando una vista delle tabelle ha finito di ricaricare il suo contenuto?
Ora possiamo riformulare questa domanda: come sapere quando una vista tabella ha finito di disporre le sue sottoview?
• Il modo più semplice è entrare nel layout della vista tabella:
class MyTableView: UITableView {
func layoutSubviews() {
super.layoutSubviews()
// the displayed cells are loaded
}
}
Si noti che questo metodo viene chiamato più volte nel ciclo di vita della vista tabella. A causa del comportamento di scorrimento e dequeue della vista tabella, le celle vengono modificate, rimosse e aggiunte spesso. Ma funziona, subito dopo il super.layoutSubviews()caricamento delle celle. Questa soluzione equivale ad attendere l' willDisplayevento dell'ultimo percorso dell'indice. Questo evento viene chiamato durante l'esecuzione dilayoutSubviews vista tabella per ogni cella aggiunta.
• Un altro modo è quello di essere chiamato quando l'app termina un passaggio di layout.
Come descritto nella documentazione , è possibile utilizzare un'opzione di UIView.animate(withDuration:completion):
tableView.reloadData()
UIView.animate(withDuration: 0) {
// layout done
}
Questa soluzione funziona ma lo schermo si aggiornerà una volta tra il momento in cui il layout viene eseguito e il momento in cui viene chiamato il blocco. Questo è equivalente alla DispatchMain.asyncsoluzione ma specificato.
• In alternativa, preferirei forzare il layout della vista tabella
Esiste un metodo dedicato per forzare qualsiasi vista a calcolare immediatamente i suoi frame di subview layoutIfNeeded:
tableView.reloadData()
table.layoutIfNeeded()
// layout done
Fare attenzione, tuttavia, rimuovendo il caricamento lento utilizzato dal sistema. La chiamata ripetuta di questi metodi potrebbe creare problemi di prestazioni. Assicurati che non vengano chiamati prima di calcolare il frame della vista tabella, altrimenti la vista tabella verrà caricata di nuovo e non riceverai alcuna notifica.
Penso che non ci sia una soluzione perfetta. Le classi di sottoclasse potrebbero portare a problemi. Un passaggio di layout inizia dall'alto e va verso il basso, quindi non è facile ricevere notifiche quando tutto il layout è terminato. E layoutIfNeeded()potrebbe creare problemi di prestazioni, ecc. Ma conoscendo queste opzioni, dovresti essere in grado di pensare a un'alternativa adatta alle tue esigenze.