Estoy tratando de desplazarme hasta la parte inferior de un UITableView después de que se realiza [self.tableView reloadData]
Originalmente tuve
[self.tableView reloadData]
NSIndexPath* indexPath = [NSIndexPath indexPathForRow: ([self.tableView numberOfRowsInSection:([self.tableView numberOfSections]-1)]-1) inSection: ([self.tableView numberOfSections]-1)];
[self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionBottom animated:YES];
Pero luego leí que reloadData es asíncrono, por lo que el desplazamiento no ocurre desde entonces self.tableView
, [self.tableView numberOfSections]
y [self.tableView numberOfRowsinSection
todos son 0.
¡Gracias!
Lo extraño es que estoy usando:
[self.tableView reloadData];
NSLog(@"Number of Sections %d", [self.tableView numberOfSections]);
NSLog(@"Number of Rows %d", [self.tableView numberOfRowsInSection:([self.tableView numberOfSections]-1)]-1);
En la consola, devuelve Sections = 1, Row = -1;
Cuando hago exactamente los mismos NSLogs en cellForRowAtIndexPath
, obtengo Sections = 1 y Row = 8; (8 es correcto)
Respuestas:
La recarga ocurre durante la siguiente pasada de diseño, que normalmente ocurre cuando devuelve el control al ciclo de ejecución (después de, digamos, la acción del botón o lo que sea que regrese).
Entonces, una forma de ejecutar algo después de que la vista de tabla se vuelve a cargar es simplemente forzar a la vista de tabla a realizar el diseño de inmediato:
Otra forma es programar su código de diseño posterior para que se ejecute más tarde usando
dispatch_async
:ACTUALIZAR
Luego de una investigación más profunda, encuentro que la vista de tabla envía
tableView:numberOfSections:
ytableView:numberOfRowsInSection:
a su fuente de datos antes de regresarreloadData
. Si el delegado implementatableView:heightForRowAtIndexPath:
, la vista de tabla también envía eso (para cada fila) antes de regresarreloadData
.Sin embargo, la vista de tabla no envía
tableView:cellForRowAtIndexPath:
otableView:headerViewForSection
hasta la fase de diseño, que ocurre de forma predeterminada cuando devuelve el control al ciclo de ejecución.También encuentro que en un pequeño programa de prueba, el código en su pregunta se desplaza correctamente hasta la parte inferior de la vista de tabla, sin que yo haga nada especial (como enviar
layoutIfNeeded
o usardispatch_async
).fuente
dispatch_async(dispatch_get_main_queue())
No se garantiza que el método funcione. Veo un comportamiento no determinista con él, en el que a veces el sistema ha completado las subvistas de diseño y la representación de la celda antes del bloque de finalización, y a veces después. Publicaré una respuesta que funcionó para mí a continuación.dispatch_async(dispatch_get_main_queue())
no siempre trabajando. Ver resultados aleatorios aquí.NSRunLoop
. Un ciclo de ejecución tiene diferentes fases, y puede programar una devolución de llamada para una fase específica (usando aCFRunLoopObserver
). UIKit programa el diseño para que ocurra durante una fase posterior, después de que regrese su controlador de eventos.Rápido:
C objetivo:
fuente
tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
método e insertando en mi anulación todo lo que quería notificar que la recarga había terminado.A partir de Xcode 8.2.1, iOS 10 y Swift 3,
Puede determinar el final
tableView.reloadData()
fácilmente utilizando un bloque CATransaction:Lo anterior también funciona para determinar el final de reloadData () de UICollectionView y reloadAllComponents () de UIPickerView.
fuente
beginUpdates
yendUpdates
llamadas.setCompletionBlock
minumberOfSections
muestra 2 ... hasta ahora todo bien. Sin embargo, si por dentrosetCompletionBlock
lo hagotableView.headerView(forSection: 1)
,nil
¡ vuelve ! Por lo tanto, creo que este bloqueo ocurre antes de la recarga o captura algo antes o estoy haciendo algo mal. FYI ¡Intenté la respuesta de Tyler y funcionó! @FattieEl
dispatch_async(dispatch_get_main_queue())
método anterior se no se garantiza que funcione . Veo un comportamiento no determinista con él, en el que a veces el sistema ha completado las subvistas de diseño y la representación de la celda antes del bloque de finalización, y a veces después.Aquí hay una solución que funciona al 100% para mí, en iOS 10. Requiere la capacidad de instanciar UITableView o UICollectionView como una subclase personalizada. Aquí está la solución UICollectionView, pero es exactamente lo mismo para UITableView:
CustomCollectionView.h:
CustomCollectionView.m:
Ejemplo de uso:
Vea aquí una versión rápida de esta respuesta
fuente
layoutSubviews
, debe establecersenil
como llamadas posteriores alayoutSubviews
, no necesariamente debido a quereloadData
se llama, dará como resultado que el bloque se ejecute, ya que existe una fuerte referencia, que no es el comportamiento deseado.reloadDataCompletionBlock
una matriz de bloques e iterar sobre ellos en la ejecución y vaciar la matriz después de eso.Tuve los mismos problemas que Tyler Sheaffer.
Implementé su solución en Swift y resolvió mis problemas.
Swift 3.0:
Swift 2:
Ejemplo de uso:
fuente
if let
diciendoreloadDataCompletionBlock?()
que llamará iff no nil Tyself.reloadDataCompletionBlock? { completion() }
debería haber sidoself.reloadDataCompletionBlock?()
Y una
UICollectionView
versión, basada en la respuesta de kolaworld:https://stackoverflow.com/a/43162226/1452758
Necesita pruebas. Funciona hasta ahora en iOS 9.2, Xcode 9.2 beta 2, con desplazamiento de un collectionView a un índice, como un cierre.
Uso:
fuente
Parece que la gente todavía está leyendo esta pregunta y las respuestas. B / c de eso, estoy editando mi respuesta para eliminar la palabra Synchronous que es realmente irrelevante para esto.
When [tableView reloadData]
devuelve, las estructuras de datos internos detrás de tableView se han actualizado. Por lo tanto, cuando se complete el método, puede desplazarse con seguridad hasta la parte inferior. Verifiqué esto en mi propia aplicación. La respuesta ampliamente aceptada por @ rob-mayoff, aunque también es confusa en términos, reconoce lo mismo en su última actualización.Si
tableView
no se desplaza hacia abajo, es posible que tenga un problema con otro código que no haya publicado. ¿Tal vez está cambiando los datos después de que se completa el desplazamiento y no está recargando y / o desplazándose al fondo entonces?Agregue algunos registros de la siguiente manera para verificar que los datos de la tabla sean correctos después
reloadData
. Tengo el siguiente código en una aplicación de muestra y funciona perfectamente.fuente
reloadData
No es sincrónico. Solía ser - mira esta respuesta: stackoverflow.com/a/16071589/193896reloadData
retornos.reloadData
. Use mi caso de pruebaviewWillAppear
para aceptar lascrollToRowAtIndexPath:
línea b / c que no tiene sentido sitableView
no se muestra. Verá quereloadData
actualizó los datos almacenados en caché en latableView
instancia y quereloadData
es sincrónico. Si se refiere a otrostableView
métodos de delegado llamados cuandotableView
se está diseñando, no se los llamará sitableView
no se muestra. Si no entiendo su escenario, explique.Utilizo este truco, bastante seguro de que ya lo publiqué en un duplicado de esta pregunta:
fuente
En realidad, este resolvió mi problema:
fuente
Intenta de esta manera funcionará
Ejecutaré cuando la mesa esté completamente cargada
Otra solución es que puedes subclasificar UITableView
fuente
Terminé usando una variación de la solución de Shawn:
Cree una clase UITableView personalizada con un delegado:
Luego, en mi código, uso
También asegúrese de establecer su vista de tabla en CustomTableView en el generador de interfaces:
fuente
En Swift 3.0 + podemos crear una extensión
UITableView
con un meescaped Closure
gusta a continuación:Y úsalo como Abajo donde quieras:
Espero que esto ayude a alguien. ¡salud!
fuente
Detalles
Solución
Uso
Muestra completa
Resultados
fuente
Solo para ofrecer otro enfoque, basado en la idea de que la finalización sea la celda 'última visible' a la que se enviará
cellForRow
.Un posible problema es: si
reloadData()
ha finalizado antes de quelastIndexPathToDisplay
se establezca, la celda 'última visible' se mostrará antes de quelastIndexPathToDisplay
se establezca y no se llamará a la finalización (y estará en estado de 'espera'):Si retrocedemos, podríamos terminar con la activación activada al desplazarse antes
reloadData()
.fuente
Prueba esto:
El color de tableView cambiará de negro a verde solo después de
reloadData()
que se complete la función.fuente
Puede usar la función performBatchUpdates de uitableview
Así es como puedes lograr
fuente
Crear una extensión reutilizable de CATransaction:
Ahora creando una extensión de UITableView que usaría el método de extensión de CATransaction:
Uso:
fuente
Puede usarlo para hacer algo después de recargar datos:
fuente
Intenta establecer retrasos:
fuente