UIScrollView desplazamiento incorrecto con layout automático

Tengo una configuration de vista bastante simple:

Un UIViewController , con un UIScrollView secundario y UIImageView en este UIScrollView . Puse el UIImageView con una altura suficiente para salir del área visible (es decir, mayor a 1024pt ), y establezco el Bottom space to superview restricción de UIImageView de mi UIImageView a un valor positivo fijo ( 20 por ejemplo).

diseño del proyecto

Toda la configuration funciona como se esperaba, la image se desplaza muy bien en su elemento principal. Excepto cuando la vista se desplaza (el efecto es más visible si se desplaza al final de la vista), luego desaparece y aparece de nuevo (cambió a otra vista y volvió), se restaura el valor de desplazamiento, pero el contenido de la vista La vista de desplazamiento se mueve a la parte superior exterior de su vista principal.

Esto no es fácil de explicar, intentaré dibujarlo: Representación visual del párrafo anterior.

Si quieres probar / ver la fuente (o el guión gráfico, no edité una sola línea de código). Puse una pequeña demostración en mi github: https://github.com/guillaume-algis/iOSAutoLayoutScrollView

Leí el cambio de iOS 6 y la explicación sobre este tema en particular, y creo que esta es la implementación correcta de la segunda opción (layout automático puro), pero en este caso, ¿por qué el UIScrollView comporta de forma tan errática? Me estoy perdiendo de algo ?

EDIT : Este es exactamente el mismo problema que # 12580434 uiscrollviewautolayout-issue . Las respuestas son solo soluciones, ya que ¿alguien encontró una forma adecuada de solucionar esto o es un error de iOS?

EDIT 2 : encontré otra solución, que mantiene la position de desplazamiento en el mismo estado que el usuario lo dejó (esta es una mejora sobre la respuesta aceptada de 12580434 ):

 @interface GAViewController () @property CGPoint tempContentOffset; @end @implementation GAViewController -(void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; self.tempContentOffset = self.mainScrollView.contentOffset; self.scrollView.contentOffset = CGPointZero; } -(void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; self.scrollView.contentOffset = self.tempContentOffset; } 

Esto básicamente guarda el desplazamiento en viewWillAppear , lo restablece al origen y luego restaura el valor en viewDidAppear . El problema parece ocurrir entre estas dos llamadas, pero no puedo encontrar su origen.

Sí, sucedió algo extraño con UIScrollView en un entorno de reproducción automática pura. Al volver a leer las notas de la versión de iOS SDK 6.0 por vigésima vez, descubrí que:

Tenga en count que puede hacer que una subview de la vista de desplazamiento parezca flotar (no desplazarse) sobre el otro contenido de desplazamiento creando restricciones entre la vista y una vista fuera del subtree de la vista de desplazamiento, como la vista de supervisión de la vista de desplazamiento.

Solución

Conecte su subvista a la vista exterior. En otras palabras, a la vista en la que scrollview está embedded.

Como IB no nos permite establecer restricciones entre imageView y una vista fuera del subtree de la vista de desplazamiento, como la vista de supervisión de la vista de desplazamiento, entonces lo he hecho en código.

 - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. [self.view removeConstraints:[self.view constraints]]; [self.scrollView removeConstraints:[self.scrollView constraints]]; [self.imageView removeConstraints:[self.imageView constraints]]; [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"|[_scrollView]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(_scrollView)]]; [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[_scrollView]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(_scrollView)]]; [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"|[_imageView(700)]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(_imageView)]]; [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[_imageView(1500)]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(_imageView)]]; } 

Y vau! ¡Funciona!

La edición no funcionó para mí. Pero esto funcionó:

 -(void)viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; self.tempContentOffset = self.scrollView.contentOffset; self.scrollView.contentOffset = CGPointZero; } - (void)viewDidLayoutSubviews { [super viewDidLayoutSubviews]; self.scrollView.contentOffset = self.tempContentOffset; } 

Para mí, fui al IB y presioné mi controller de vista que contiene la vista de desplazamiento. Luego fui a Inspector de attributes -> Controlador de vista -> Extender bordes -> Desmarca "Debajo de barras superiores" y "Debajo barras inferiores".

Solución simple encontrada, simplemente poner

 [self setAutomaticallyAdjustsScrollViewInsets:NO]; 

en su vista ViewControllers viewDidLoad método

 - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view from its nib. [self setAutomaticallyAdjustsScrollViewInsets:NO]; } 

Tuve un problema similar al usar un UIScrollView en un UIViewController con un espacio extra superior que no estaba visible en Storyboard. La solución para mí era desmarcar las "Inscripciones de vista de desplazamiento de desplazamiento" en las properties del guión gráfico de ViewController: ver la respuesta Espacio adicional (inserto) en la vista de desplazamiento en time de ejecución

Agregue un contenido global de propiedad, click Desfavor y guarde el contenido actual de Desfavor en viewDidDisappear. Una vez que regrese el método viewDidLayoutSubviews se llamará y podrá configurar su contenido original Offset.

 - (void)viewDidLayoutSubviews { [super viewDidLayoutSubviews]; [self.scrollView setContentOffset:self.contentOffset animated:FALSE]; } - (void)viewDidDisappear:(BOOL)animated { [super viewDidDisappear:animated]; self.contentOffset = self.scrollView.contentOffset; [self.scrollView setContentOffset:CGPointMake(0, 0) animated:FALSE]; } 

Parece que el problema resuelto con dispatch_async durante la viewWillAppear:

 - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; CGPoint originalContentOffset = self.scrollView.contentOffset; self.scrollView.contentOffset = CGPointZero; dispatch_async(dispatch_get_main_queue(), ^{ self.scrollView.contentOffset = originalContentOffset; }); }