contentOffset no actualizado en UICollectionView si scrollToItemAtIndexPath se llama dentro de viewWillAppear


Tengo un UICollectionView que se utiliza para simular el nuevo calendario en iOS 7. Esta vista de colección está dentro de un controlador que tiene una propiedad SelectedDate. Siempre que se establezca la propiedad SelectedDate, la vista de colección debe desplazarse hasta la fecha en la vista de colección.

La vista del controlador de calendario también se asegurará de que la fecha seleccionada sea visible porque este controlador se almacena en caché y se reutiliza.

-(void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];

    [self.calendarView scrollToDate:[self selectedDate] animated:NO];
}

El problema es que la primera vez que el controlador de calendario se muestra el desplazamiento no funciona. El contentOffset de la vista de colección no se actualiza.

Mi solución actual es programar el desplazamiento para que se produzca en el siguiente bucle de ejecución utilizando

dispatch_after(DISPATCH_TIME_NOW, dispatch_get_main_queue(), ^(void)
{
        // Scroll to the date.
    });

Parece que cuando el UICollectionView no está en una ventana no puede desplazarse. Programar el desplazamiento para que ocurra en el siguiente bucle de ejecución asegúrese de que la vista se haya agregado a la ventana y se pueda desplazar correctamente.

¿Alguien más ha experimentado este problema y lo que su soluciones?

Author: Reid Main, 2013-10-01

4 answers

Si está utilizando auto layout, el problema puede ser que las restricciones aún no hayan establecido los marcos. Intente llamar al método scrollToDate: en viewDidLayoutSubviews (sin dispatch_after).

@interface CustomViewController ()

@property (nonatomic) BOOL isFirstTimeViewDidLayoutSubviews; // variable name could be re-factored

@property (nonatomic, weak) IBOutlet UIScrollView *scrollView;

@end

@implementation CustomViewController

- (void)viewDidLoad
{
    [super viewDidLoad];

    self.isFirstTimeViewDidLayoutSubviews = YES;
}

- (void)viewDidLayoutSubviews
{
    // only after layoutSubviews executes for subviews, do constraints and frames agree (WWDC 2012 video "Best Practices for Mastering Auto Layout")

    if (self.isFirstTimeViewDidLayoutSubviews) {

        // execute geometry-related code...

        // good place to set scroll view's content offset, if its subviews are added dynamically (in code)

        self.isFirstTimeViewDidLayoutSubviews = NO;
    }
 22
Author: bilobatum,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-10-11 22:22:17

Siempre puede forzar auto-layout a layout.

override func viewWillAppear(animated: Bool) {
    super.viewWillAppear(animated)
    self.view.layoutIfNeeded()
    self.collectionView.scrollToItemAtIndexPath......
}
 15
Author: maxpower,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-01-30 18:56:11

La respuesta de Bilobatum es correcta! Escribo esto porque no tengo reputación para comentar... :/

Probé la respuesta de bilobatum en mi proyecto, ¡y funcionó perfectamente! Mi código:

-(void)viewDidLayoutSubviews{
    [super viewDidLayoutSubviews];
    if (currentOffset.y != 999) {
        [collectionView setContentOffset:currentOffset animated:NO];
    }
}

CurrentOsset es un CGPoint inicializado con valores x = 0 e y = 999(CGPoint currentOffset = {0,999};)

En el método viewWillDisappear guardo el contentOffset de collectionView en el currentOffset. De esta manera si navego al controlador que tiene la collectionView y navego hasta allí antes, siempre tendré la última posición.

El código que funcionará para usted:

-(void)viewDidLayoutSubviews{
    [super viewDidLayoutSubviews];
    [self.calendarView scrollToDate:[self selectedDate] animated:NO];
}

Gracias bilobatum por la respuesta!

 3
Author: kennyevo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-10-07 13:33:07

Usando-viewDidLayoutSubviews creó un bucle infinito que hizo que la solución fuera demasiado complicada.
En su lugar, solo agregué un pequeño retraso para permitir que las restricciones se creen antes del desplazamiento:

- (void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];

    if ([self.scheduleDate isThisWeek]) [self.calendarLayout performSelector:@selector(scrollToCurrentTime) withObject:nil afterDelay:1];
}
 0
Author: Yariv,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-11-03 20:25:46