¿Recibir una notificación cuando UITableView haya terminado de solicitar datos?

108

¿Hay alguna forma de averiguar cuándo UITableView ha terminado de solicitar datos de su fuente de datos?

Ninguno de los viewDidLoad/ viewWillAppear/ viewDidAppearmétodos del controlador de vista asociado (UITableViewController ) es útil aquí, ya que todos se activan demasiado pronto. Ninguno de ellos (enteramente comprensible) garantiza que las consultas a la fuente de datos hayan finalizado por el momento (por ejemplo, hasta que se desplace la vista).

Una solución que he encontrado es que llamar reloadDataen viewDidAppear, ya que, cuando reloadDataregresa, la vista de tabla está garantizado de haber terminado la consulta de la fuente de datos tanto como lo necesita por el momento.

Sin embargo, esto parece bastante desagradable, ya que supongo que está provocando que se solicite la misma información a la fuente de datos dos veces (una vez automáticamente y otra debido a la reloadData llamada) cuando se carga por primera vez.

La razón por la que quiero hacer esto es que quiero preservar la posición de desplazamiento del UITableView, pero hasta el nivel de píxel, no solo hasta la fila más cercana.

Al restaurar la posición de desplazamiento (usando scrollRectToVisible:animated:), necesito que la vista de tabla ya tenga suficientes datos, o de lo contrario, la scrollRectToVisible:animated:llamada al método no hace nada (que es lo que sucede si coloca la llamada por sí sola en cualquiera de viewDidLoad, viewWillAppearo viewDidAppear).

kennethmac2000
fuente
Parece que está buscando algo similar a esto stackoverflow.com/a/11672379/2082172 .
Timur Kuchkarov
En mi experiencia, UITableView puede almacenar en caché las llamadas a reloadData, al igual que almacena en caché las llamadas para insertar / eliminar filas y otras cosas. UITableView llamará al delegado de la fuente de datos cuando esté listo, dependiendo del uso de la CPU y otras cosas.
Walt Sellers

Respuestas:

61

Esta respuesta ya no parece funcionar, debido a algunos cambios realizados en la implementación de UITableView desde que se escribió la respuesta. Ver este comentario: ¿ Recibir una notificación cuando UITableView haya terminado de solicitar datos?

He estado jugando con este problema para un par de días y creo que la subclasificación UITableView's reloadDataes el mejor enfoque:

- (void)reloadData {

    NSLog(@"BEGIN reloadData");

    [super reloadData];

    NSLog(@"END reloadData");

}

reloadDatano termina antes de que la tabla haya terminado de recargar sus datos. Entonces, cuando el segundoNSLog se activa , la vista de tabla ha terminado de solicitar datos.

He subclasificado UITableViewpara enviar métodos al delegado antes y después reloadData. Funciona a las mil maravillas.

Eric MORAND
fuente
2
Tengo que decir que esta parece ser la mejor solución. Simplemente lo implementé y, como usted dice, funciona como un encanto. ¡Gracias!
teoría
2
@EricMORAND Dices que "reloadData no termina antes de que la tabla haya terminado de recargar sus datos". ¿Puede aclarar lo que quiere decir con eso? Encuentro que reloadDataregresa inmediatamente y veo "END reloadData" antes de que las celdas se vuelvan a cargar (es decir, antes de UITableViewDataSourcellamar a los métodos). Mi experimentación demuestra exactamente lo contrario de lo que dices. Debo entender mal lo que estás tratando de decir.
Rob
3
¿No es la respuesta de Eric lo mismo que no implementar (leer, no anular) reloaddata, llamar a reloaddata (que será esencialmente [super reloaddata], y luego, después de llamarlo, hacer lo que quieras al finalizar?
Nirav Bhatt
5
Érase una vez, reloadData completó el proceso de carga antes de que terminara. Pero Apple lo cambió en algún momento. Ahora la clase UITableView puede almacenar en caché la llamada reloadData con todas las llamadas de inserción y eliminación de filas. Si observa la declaración de @interface para UITableView, encontrará el miembro de NSMutableArray _reloadItems justo debajo de _insertItems y _deleteItems. (Tuve que volver a trabajar el código que heredé debido a este cambio)
Walt Sellers
3
Publicar el código de finalización en un bloque en la cola principal después de abrir [super reloadData]funciona para mí: dispatch_async(dispatch_get_main_queue(), ^{NSLog(@"reload complete");});. Básicamente, salta los bloques que se publican en la vista de tabla reloadData.
Timothy Moose
53

Tuve un mismo escenario en mi aplicación y pensé en publicar mi respuesta para ustedes, ya que otras respuestas mencionadas aquí no funcionan para mí para iOS7 y versiones posteriores.

Finalmente, esto es lo único que funcionó para mí.

[yourTableview reloadData];

dispatch_async(dispatch_get_main_queue(),^{
        NSIndexPath *path = [NSIndexPath indexPathForRow:yourRow inSection:yourSection];
        //Basically maintain your logic to get the indexpath
        [yourTableview scrollToRowAtIndexPath:path atScrollPosition:UITableViewScrollPositionTop animated:YES];
 });

Actualización rápida:

yourTableview.reloadData()
dispatch_async(dispatch_get_main_queue(), { () -> Void in
    let path : NSIndexPath = NSIndexPath(forRow: myRowValue, inSection: mySectionValue)
    //Basically maintain your logic to get the indexpath
    yourTableview.scrollToRowAtIndexPath(path, atScrollPosition: UITableViewScrollPosition.Top, animated: true)

})

Entonces, ¿cómo funciona esto?

Básicamente, cuando realizas una recarga, el subproceso principal se vuelve ocupado, por lo que en ese momento cuando hacemos un envío de subproceso asíncrono, el bloque esperará hasta que el subproceso principal termine. Entonces, una vez que la vista de tabla se haya cargado por completo, el hilo principal terminará y enviará nuestro bloque de método

Probado en iOS7 e iOS8 y funciona increíble;)

Actualización para iOS9: esto funciona bien en iOS9 también. He creado un proyecto de muestra en github como POC. https://github.com/ipraba/TableReloadingNotifier

Adjunto la captura de pantalla de mi prueba aquí.

Entorno probado: simulador iOS9 iPhone6 ​​de Xcode7

ingrese la descripción de la imagen aquí

iPrabu
fuente
8
mejor respuesta que he encontrado!
Nikolay Shubenkov
@Gon hice una prueba en iOS9 y funciona bien. ¿Puede consultar github.com/ipraba/TableReloadingNotifier
iPrabu
Funciona bien en mi emulador pero no parece funcionar en el dispositivo real. ¿Alguien más tiene este problema?
sosale151
1
¡Esta solución finalmente funciona para mí! funciona bien en iOS 9
Fuerte
1
Lo he probado en Real Device, un iPhone 6s. Funciona bien también.
Fuerte
25

EDITAR: Esta respuesta en realidad no es una solución. Probablemente parece funcionar al principio porque la recarga puede suceder bastante rápido, pero de hecho, el bloque de finalización no se llama necesariamente después de que los datos hayan terminado de recargarse por completo, porque reloadData no se bloquea. Probablemente debería buscar una mejor solución.

Para ampliar la respuesta de @Eric MORAND, pongamos un bloque de finalización. ¿A quién no le encanta un bloque?

@interface DUTableView : UITableView

   - (void) reloadDataWithCompletion:( void (^) (void) )completionBlock;

@end

y...

#import "DUTableView.h"

@implementation DUTableView

- (void) reloadDataWithCompletion:( void (^) (void) )completionBlock {
    [super reloadData];
    if(completionBlock) {
        completionBlock();
    }
}

@end

Uso:

[self.tableView reloadDataWithCompletion:^{
                                            //do your stuff here
                                        }];
bandejapaisa
fuente
2
No puedo tener suficientes bloques. ¡Quién necesita un delegado cuando tienes un buen bloque!
bandejapaisa
Me gusta esto, pero ¿qué pasa con los momentos en que el sistema llama a reloadData (), por ejemplo, cuando se muestra la tabla por primera vez?
Simétrico
12
Esta no es la solución El bloque de finalización inicia la ejecución antes de cellForRowAtIndexPath
zvjerka24
1
Esto no funcionará; reloadData no es un método de bloqueo. Este código se llama inmediatamente después de llamar a reloadData, incluso si las celdas aún no se han recargado. Además, mire este código y verá que también puede poner su código después de reloadData .
colinta
1
Esto funciona para mí con un pequeño cambio. En lugar de llamar directamente el bloque de terminación, lo llaman en un bloque publicado en la cola principal: dispatch_async(dispatch_get_main_queue(), ^{completionBlock();});. Básicamente, esto salta los bloques publicados por la vista de tabla en reloadData.
Timothy Moose
11

reloadData solo solicita datos para las celdas visibles. Dice, para recibir una notificación cuando se cargue una parte específica de su tabla, conecte el tableView: willDisplayCell:método.

- (void) reloadDisplayData
{
    isLoading =  YES;
    NSLog(@"Reload display with last index %d", lastIndex);
    [_tableView reloadData];
    if(lastIndex <= 0){
    isLoading = YES;
    //Notify completed
}

- (void) tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
    if(indexPath.row >= lastIndex){
    isLoading = NO;
    //Notify completed
}
Codificador negro
fuente
1
No estoy seguro de si esto funciona. El último índice es el final de los datos de la tabla (por ejemplo, 100 registros) pero la tabla solo mostrará lo que está visible en la pantalla (por ejemplo, 8 registros).
Travis M.
10

Esa es mi solucion. 100% funciona y se utiliza en muchos proyectos. Es una subclase simple de UITableView.

@protocol MyTableViewDelegate<NSObject, UITableViewDelegate>
@optional
- (void)tableViewWillReloadData:(UITableView *)tableView;
- (void)tableViewDidReloadData:(UITableView *)tableView;
@end

@interface MyTableView : UITableView {
    struct {
        unsigned int delegateWillReloadData:1;
        unsigned int delegateDidReloadData:1;
        unsigned int reloading:1;
    } _flags;
}
@end

@implementation MyTableView
- (id<MyTableViewDelegate>)delegate {
    return (id<MyTableViewDelegate>)[super delegate];
}

- (void)setDelegate:(id<MyTableViewDelegate>)delegate {
    [super setDelegate:delegate];
    _flags.delegateWillReloadData = [delegate respondsToSelector:@selector(tableViewWillReloadData:)];
    _flags.delegateDidReloadData = [delegate    respondsToSelector:@selector(tableViewDidReloadData:)];
}

- (void)reloadData {
    [super reloadData];
    if (_flags.reloading == NO) {
        _flags.reloading = YES;
        if (_flags.delegateWillReloadData) {
            [(id<MyTableViewDelegate>)self.delegate tableViewWillReloadData:self];
        }
        [self performSelector:@selector(finishReload) withObject:nil afterDelay:0.0f];
    }
}

- (void)finishReload {
    _flags.reloading = NO;
    if (_flags.delegateDidReloadData) {
        [(id<MyTableViewDelegate>)self.delegate tableViewDidReloadData:self];
    }
}

@end

Es similar a la solución de Josh Brown con una excepción. No se necesita ningún retraso en el método performSelector. No importa cuánto tiempo reloadDatatarde. tableViewDidLoadData:Siempre dispara cuando tableViewtermina de preguntar dataSource cellForRowAtIndexPath.

Incluso si no desea crear una subclase UITableView, simplemente puede llamar [performSelector:@selector(finishReload) withObject:nil afterDelay:0.0f]y se llamará a su selector justo después de que la tabla termine de recargarse. Pero debe asegurarse de que se llame al selector solo una vez por llamada a reloadData:

[self.tableView reloadData];
[self performSelector:@selector(finishReload) withObject:nil afterDelay:0.0f];

Disfrutar. :)

Mark Kryzhanouski
fuente
1
Usar la llamada performSelector es brillante. Simple y funcional, gracias
Julia
Esto es absolutamente asombroso. He estado discutiendo con esto durante días. ¡Gracias!
David Carrico
@MarkKryzhanouski, ¿has probado en iOS 9?
Victor
@MarkKryzhanouski, ¡gracias por los prontos comentarios! Funciona muy bien en iOS 7 y 8.
Victor
Las soluciones performSelectoro la ejecución en el hilo principal dispatch_asynchno funcionan en iOS 9 .
Manuel
8

Esta es una respuesta a una pregunta ligeramente diferente: también necesitaba saber cuándo UITableViewhabía terminado de llamar cellForRowAtIndexPath(). Me subclases layoutSubviews()(gracias @Eric MORAND) y se añadió una devolución de llamada delegado:

SDTableView.h:

@protocol SDTableViewDelegate <NSObject, UITableViewDelegate>
@required
- (void)willReloadData;
- (void)didReloadData;
- (void)willLayoutSubviews;
- (void)didLayoutSubviews;
@end

@interface SDTableView : UITableView

@property(nonatomic,assign) id <SDTableViewDelegate> delegate;

@end;

SDTableView.m:

#import "SDTableView.h"

@implementation SDTableView

@dynamic delegate;

- (void) reloadData {
    [self.delegate willReloadData];

    [super reloadData];

    [self.delegate didReloadData];
}

- (void) layoutSubviews {
    [self.delegate willLayoutSubviews];

    [super layoutSubviews];

    [self.delegate didLayoutSubviews];
}

@end

Uso:

MyTableViewController.h:

#import "SDTableView.h"
@interface MyTableViewController : UITableViewController <SDTableViewDelegate>
@property (nonatomic) BOOL reloadInProgress;

MyTableViewController.m:

#import "MyTableViewController.h"
@implementation MyTableViewController
@synthesize reloadInProgress;

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {

    if ( ! reloadInProgress) {
        NSLog(@"---- numberOfSectionsInTableView(): reloadInProgress=TRUE");
        reloadInProgress = TRUE;
    }

    return 1;
}

- (void)willReloadData {}
- (void)didReloadData {}
- (void)willLayoutSubviews {}

- (void)didLayoutSubviews {
    if (reloadInProgress) {
        NSLog(@"---- layoutSubviewsEnd(): reloadInProgress=FALSE");
        reloadInProgress = FALSE;
    }
}

NOTAS: Dado que esta es una subclase de la UITableViewcual ya tiene una propiedad de delegado que apunta, MyTableViewControllerno es necesario agregar otra. El "delegado @dynamic" le dice al compilador que use esta propiedad. (Aquí hay un enlace que describe esto: http://farhadnoorzay.com/2012/01/20/objective-c-how-to-add-delegate-methods-in-a-subclass/ )

La UITableViewpropiedad en MyTableViewControllerdebe cambiarse para usar la nueva SDTableViewclase. Esto se hace en el Inspector de identidad de Interface Builder. Seleccione el UITableViewinterior del UITableViewControllery establezca su "Clase personalizada" en SDTableView.

Simétrico
fuente
¿Dónde configura su MyTableViewController para que sea el delegado de SDTableView? ¿Cómo es posible establecer una propiedad de nombre "delegado" en SDTableView, cuando su superclase ya tiene una propiedad de ese nombre (UITableView.delegate)? ¿Cómo se une su SDTableView personalizado a la propiedad MyTableViewController.tableView cuando la propiedad es del tipo "UITablewView" y el objeto (una instancia de SDTableView) es del tipo "SDTableView"? Estoy luchando contra el mismo problema, así que espero que haya una solución a estos problemas :)
Earl Grey
En el código de Symmetric (SDTableView), ¿no debería reloadInProgress establecerse en FALSE en didReloadData en lugar de didLayoutSubviews? Porque reloadData se llama después de layoutSubviews y la carga no debe considerarse realizada antes de que finalice reloadData.
tzuchien.chiu
Esto no funciona para iOS 8, tenía que incorporar la respuesta de iPrabu .
Stunner
6

Encontré algo similar para recibir una notificación de cambio contentSizede TableView. Creo que eso también debería funcionar aquí, ya que contentSize también cambia con la carga de datos.

Prueba esto:

Por viewDidLoadescrito,

[self.tableView addObserver:self forKeyPath:@"contentSize" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld | NSKeyValueObservingOptionPrior context:NULL];

y agregue este método a su viewController:

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    if ([keyPath isEqualToString:@"contentSize"]) {
        DLog(@"change = %@", change.description)

        NSValue *new = [change valueForKey:@"new"];
        NSValue *old = [change valueForKey:@"old"];

        if (new && old) {
            if (![old isEqualToValue:new]) {
                // do your stuff
            }
        }


    }
}

Es posible que necesite pequeñas modificaciones en la verificación de cambios. Sin embargo, esto me había funcionado.

¡Salud! :)

Abdullah Umer
fuente
1
Muy inteligente. Funciona perfectamente para mi. ¡Gracias por compartir!
DZenBot
2
¡Elegante! Lo único que agregaría es que debe eliminarse usted y un observador (quizás en el método -dealloc). O agréguese como observador en el -viewWillAppeary elimínese en el -viewWillDisapearmétodo.
Loozie
2

Aquí hay una posible solución, aunque es un truco:

[self.tableView reloadData];
[self performSelector:@selector(scrollTableView) withObject:nil afterDelay:0.3];

Donde su -scrollTableViewmétodo se desplaza por la vista de tabla con -scrollRectToVisible:animated:. Y, por supuesto, puede configurar el retraso en el código anterior de 0.3 a lo que parezca funcionar para usted. Sí, es ridículamente hacky, pero me funciona en mi iPhone 5 y 4S ...

Josh Brown
fuente
2
Puede parecer "hacky", pero este es realmente un enfoque estándar para el desplazamiento: diferirlo para el siguiente ciclo de ejecución. Cambiaría el retraso a 0, ya que funciona igual de bien y tiene menos latencia.
phatmann
No funcionó para mí con el retraso establecido en 0; 0.3 fue lo más bajo que pude obtener y aún así obtener los datos.
Josh Brown
@phatmann Esto funcionó para mí. También pude usar 0 para mi retraso. Gracias a los dos.
mcphersonjr
1

Creo que tuve algo similar. Agregué un BOOL como variable de instancia que me dice si el desplazamiento se ha restaurado y lo verifico -viewWillAppear:. Cuando no se ha restaurado, lo restauro en ese método y configuro el BOOL para indicar que recuperé el desplazamiento.

Es una especie de truco y probablemente se pueda hacer mejor, pero esto funciona para mí en este momento.

Joost
fuente
Sí, el problema es que viewWillAppear parece ser demasiado pronto (al menos en mi escenario). Intentar restaurar el desplazamiento en viewWillAppear no hace nada, a menos que agregue el truco de llamar a reloadData primero. Según tengo entendido, viewWillAppear / viewDidAppear solo se refiere a la vista de la tabla que realmente aparece; no hacen ninguna afirmación sobre si las celdas de la tabla en la vista se han enumerado o poblado ... y esto debe suceder antes de que pueda recuperarse un desplazamiento, ya que de lo contrario estaría recuperando un desplazamiento en una vista vacía (¡y entiendo por qué eso no funcionará!).
kennethmac2000
Pero tal vez quiso decir que también está forzando la carga de las celdas de la tabla primero llamando a reloadData dentro de viewWillAppear?
kennethmac2000
No, no estoy forzando una recarga. Cuando tuve el problema, traté de restaurar el desplazamiento -viewDidLoad(donde debería suceder, por supuesto) pero eso solo funcionó cuando configuré el desplazamiento animado. Al mover la configuración del desplazamiento -viewWillAppear:funcionó, pero tuve que mantener una bandera para configurarlo solo una vez. Supongo que la vista de tabla recarga sus datos una vez agregados a una vista, por lo que ya está en -loadView. ¿Está seguro de que tiene sus datos disponibles al cargar la vista? ¿O se está cargando en un hilo separado o algo así?
Joost
Bien, tal vez puedas ayudarme a entender aquí. Así es como entiendo la secuencia de llamadas cuando se crea un UITableView. 1) viewDidLoad se activa primero. Esto indica que UITableView está cargado en la memoria. 2) viewWillAppear está al lado del fuego. Esto indica que se mostrará UITableView, pero no necesariamente que todos los objetos UITableViewCell visibles serán completamente instanciados / terminados de renderizado.
kennethmac2000
2
Y todo lo anterior es cierto, la pregunta entonces es: ¿qué opciones no pirateadas tenemos para averiguar cuándo UITableView ha obtenido suficiente información de su fuente de datos para garantizar que una solicitud de desplazamiento (es decir, un scrollRectToVisible: animated: call ) ¿realmente funcionará (y no solo no hará nada)?
kennethmac2000
1

Parece que desea actualizar el contenido de la celda, pero sin los saltos repentinos que pueden acompañar a las inserciones y eliminaciones de celdas.

Hay varios artículos sobre cómo hacerlo. Este es uno.

Sugiero usar setContentOffset: animated: en lugar de scrollRectToVisible: animated: para configuraciones de píxeles perfectos de una vista de desplazamiento.

Vendedores de Walt
fuente
1

Puedes probar la siguiente lógica:

-(UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"MyIdentifier"];

    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"MyIdentifier"];
        cell.selectionStyle = UITableViewCellSelectionStyleNone;
    }

    if ( [self chkIfLastCellIndexToCreate:tableView :indexPath]){
        NSLog(@"Created Last Cell. IndexPath = %@", indexPath);
        //[self.activityIndicator hide];
        //Do the task for TableView Loading Finished
    }
    prevIndexPath = indexPath;

    return cell;
}



-(BOOL) chkIfLastCellIndexToCreate:(UITableView*)tableView : (NSIndexPath *)indexPath{

    BOOL bRetVal = NO;
    NSArray *visibleIndices = [tableView indexPathsForVisibleRows];

    if (!visibleIndices || ![visibleIndices count])
        bRetVal = YES;

    NSIndexPath *firstVisibleIP = [visibleIndices objectAtIndex:0];
    NSIndexPath *lastVisibleIP = [visibleIndices objectAtIndex:[visibleIndices count]-1];

    if ((indexPath.row > prevIndexPath.row) && (indexPath.section >= prevIndexPath.section)){
        //Ascending - scrolling up
        if ([indexPath isEqual:lastVisibleIP]) {
            bRetVal = YES;
            //NSLog(@"Last Loading Cell :: %@", indexPath);
        }
    } else if ((indexPath.row < prevIndexPath.row) && (indexPath.section <= prevIndexPath.section)) {
        //Descending - scrolling down
        if ([indexPath isEqual:firstVisibleIP]) {
            bRetVal = YES;
            //NSLog(@"Last Loading Cell :: %@", indexPath);
        }
    }
    return bRetVal;
}

Y antes de llamar a reloadData, establezca prevIndexPath en nil. Me gusta:

prevIndexPath = nil;
[mainTableView reloadData];

Probé con NSLogs y esta lógica parece estar bien. Puede personalizar / mejorar según sea necesario.

Joe M
fuente
0

finalmente he hecho que mi código funcione con esto -

[tableView scrollToRowAtIndexPath:scrollToIndex atScrollPosition:UITableViewScrollPositionTop animated:YES];

había pocas cosas que debían ser atendidas -

  1. llámalo dentro de " - (UITableViewCell *)MyTableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath"
  2. solo asegúrese de que el mensaje "scrollToRowAtIndexPath" se envíe a la instancia relevante de UITableView, que definitivamente es MyTableview en este caso.
  3. En mi caso, UIView es la vista que contiene una instancia de UITableView
  4. Además, esto se llamará para cada carga de celda. Por lo tanto, coloque una lógica dentro de "cellForRowAtIndexPath" para evitar llamar a "scrollToRowAtIndexPath" más de una vez.
smile.al.d.way
fuente
y asegúrese de que esta pieza no se llame más de una vez. si no, bloquea el desplazamiento.
smile.al.d.way
Esto puede funcionar, pero definitivamente no es elegante y no es la solución que estoy buscando. Desafortunadamente, no parece haber una mejor manera de determinar si -reloadData realmente ha terminado de obtener los datos ...
Josh Brown
0

Puede cambiar el tamaño de su vista de tabla o establecer el tamaño del contenido en este método cuando se cargan todos los datos:

- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
    tableView.frame =CGRectMake(tableView.frame.origin.x, tableView.frame.origin.y, tableView.frame.size.width, tableView.contentSize.height);
}
Banquero Mittal
fuente
0

Simplemente ejecuto el temporizador programado repetido y lo invalido solo cuando el contentSize de la tabla es mayor cuando la altura tableHeaderView (significa que hay contenido de filas en la tabla). El código en C # (monotouch), pero espero que la idea sea clara:

    public override void ReloadTableData()
    {
        base.ReloadTableData();

        // don't do anything if there is no data
        if (ItemsSource != null && ItemsSource.Length > 0)
        {
            _timer = NSTimer.CreateRepeatingScheduledTimer(TimeSpan.MinValue, 
                new NSAction(() => 
                {
                    // make sure that table has header view and content size is big enought
                    if (TableView.TableHeaderView != null &&
                        TableView.ContentSize.Height > 
                            TableView.TableHeaderView.Frame.Height)
                    {
                        TableView.SetContentOffset(
                            new PointF(0, TableView.TableHeaderView.Frame.Height), false);
                        _timer.Invalidate();
                        _timer = null;
                    }
                }));
        }
    }
Eugene Tiutiunnyk
fuente
0

¿No se UITableView layoutSubviewsllama justo antes de que la vista de tabla muestre su contenido? He notado que se llama una vez que la vista de tabla ha terminado de cargar sus datos, tal vez debería investigar en esa dirección.

Eric MORAND
fuente
0

Desde iOS 6 en adelante, el UITableviewmétodo delegado llamó:

-(void)tableView:(UITableView *)tableView willDisplayHeaderView:(UIView *)view forSection:(NSInteger)section

se ejecutará una vez que su mesa se recargue correctamente. Puede realizar la personalización según sea necesario en este método.

Bharath Vankireddy
fuente
0

La mejor solución que encontré en Swift

extension UITableView {
    func reloadData(completion: ()->()) {
        self.reloadData()
        dispatch_async(dispatch_get_main_queue()) {
            completion()
        }
    }
}
YannSteph
fuente
-1

¿Por qué no simplemente extender?

@interface UITableView(reloadComplete)
- (void) reloadDataWithCompletion:( void (^) (void) )completionBlock;
@end

@implementation UITableView(reloadComplete)
- (void) reloadDataWithCompletion:( void (^) (void) )completionBlock {
    [self reloadData];
    if(completionBlock) {
        completionBlock();
    }
}
@end

desplácese hasta el final:

[self.table reloadDataWithCompletion:^{
    NSInteger numberOfRows = [self.table numberOfRowsInSection:0];
    if (numberOfRows > 0)
    {
        NSIndexPath *indexPath = [NSIndexPath indexPathForRow:numberOfRows-1 inSection:0];
        [self.table scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionTop animated:NO];
    }
}];

No probado con muchos datos

Andrii Tishchenko
fuente