tutorial inside doesn ios uiscrollview zoom autolayout

ios - inside - UIScrollView Zoom no funciona con Autolayout



uiscrollview ios (6)

Hacer zoom con UIScrollView usando un entorno estrictamente automático no parece funcionar.

Esto es especialmente frustrante porque las notas de la versión de iOS 6 ciertamente me llevan a creer que debería hacerlo cuando escribí sobre un "enfoque de Pure Auto Layout" aquí http://developer.apple.com/library/ios/#releasenotes/General/RN-iOSSDK-6_0/_index.html

Miré las diapositivas de WWDC 2012 para las sesiones 202, 228 y 232 y no vi una respuesta para esto.

La única pregunta que he visto en Internet específicamente sobre este tema es UIScrollView zooming with Auto Layout , pero no proporciona el código del problema y no hay respuesta.

Este usuario https://stackoverflow.com/users/341994/matt ha dado muchas excelentes respuestas a las preguntas de UIScrollView autolayout e incluso ha vinculado al código en git hub, pero no he podido encontrar nada que responda a este problema allí.

Intenté reducir este problema al mínimo absoluto para dejarlo en claro.

Creé una nueva aplicación de vista única con un guión gráfico y no hice cambios en el constructor de interfaz.

Agregué un archivo de imagen grande al proyecto "pic.jpg".

SVFViewController.h

#import <UIKit/UIKit.h> @interface SVFViewController : UIViewController <UIScrollViewDelegate> @property (nonatomic) UIImageView *imageViewPointer; @end

SVFViewController.m

#import "SVFViewController.h" @interface SVFViewController () @end @implementation SVFViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. UIScrollView *scrollView = [[UIScrollView alloc] init]; UIImageView *imageView = [[UIImageView alloc] init]; [imageView setImage:[UIImage imageNamed:@"pic.jpg"]]; [self.view addSubview:scrollView]; [scrollView addSubview:imageView]; scrollView.translatesAutoresizingMaskIntoConstraints = NO; imageView.translatesAutoresizingMaskIntoConstraints = NO; self.imageViewPointer = imageView; scrollView.maximumZoomScale = 2; scrollView.minimumZoomScale = .5; scrollView.delegate = self; NSDictionary *viewsDictionary = NSDictionaryOfVariableBindings(scrollView,imageView); NSLog(@"Current views dictionary: %@", viewsDictionary); [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[scrollView]|" options:0 metrics: 0 views:viewsDictionary]]; [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[scrollView]|" options:0 metrics: 0 views:viewsDictionary]]; [scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[imageView]|" options:0 metrics: 0 views:viewsDictionary]]; [scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[imageView]|" options:0 metrics: 0 views:viewsDictionary]]; } -(UIView*)viewForZoomingInScrollView:(UIScrollView *)scrollView{ return self.imageViewPointer; } @end

Tenga en cuenta que hice un esfuerzo especial para hacer que esto se pareciese tanto al código de muestra proporcionado en las notas de la versión de iOS 6, solo haciendo lo mínimo para implementar el zoom.

Entonces, el problema?

Cuando ejecuta esta aplicación y gira en la vista de desplazamiento, todo está bien. Pero cuando hace zoom, el problema es obvio, la imagen parpadea hacia adelante y hacia atrás, y la colocación de la imagen dentro de la vista de desplazamiento se vuelve más incorrecta con cada zoom.

Parece que hay una batalla por el offset de contenido de imageView, parece que se está configurando a diferentes valores por dos cosas diferentes con cada "zoom". (un NSLog de la propiedad de compensación de contenido de imageView parece confirmar esto).

¿Qué estoy haciendo mal aquí? ¿Alguien sabe cómo implementar la propiedad de zoom dentro de un UIScrollView en un entorno totalmente automático? ¿Hay algún ejemplo de esto en algún lugar?

Por favor ayuda.


Digamos que tienes en el guión gráfico " UIImageView " dentro de " UIScrollView " dentro de " UIView ".

Vincule todas las restricciones en " UIScrollView " con el controlador de vista + las dos restricciones en UIView ( Horizontal Space - Scroll View - View y Horizontal Space - Scroll View - View ).

establezca el delegado AS de controlador de vista para " UIScrollView ".

Luego implementa este código:

@interface VC () <UIScrollViewDelegate> @property (weak, nonatomic) IBOutlet UIScrollView *scrollView; @property (weak, nonatomic) IBOutlet UIImageView *imageView; @property (strong, nonatomic) IBOutletCollection(NSLayoutConstraint) NSArray* constraints; @end @implementation FamilyTreeImageVC - (void)viewDidLoad { [super viewDidLoad]; [self.scrollView removeConstraints:self.constraints]; } - (UIView*)viewForZoomingInScrollView:(UIScrollView *)scrollView { return self.imageView; } -(void) scrollViewDidZoom:(UIScrollView *)scrollView { CGRect cFrame = self.imageView.frame; cFrame.origin = CGPointZero; self.imageView.frame = cFrame; }


Entonces esto es lo que logré resolver.

Aquí está el original con mis cambios:

@interface ScrollViewZoomTestViewController () <UIScrollViewDelegate> @property (nonatomic, strong) UIImageView* imageViewPointer; // These properties are new @property (nonatomic, strong) NSMutableArray* imageViewConstraints; @property (nonatomic) BOOL imageViewConstraintsNeedUpdating; @property (nonatomic, strong) UIScrollView* scrollViewPointer; @end @implementation ScrollViewZoomTestViewController - (void)viewDidLoad { [super viewDidLoad]; UIScrollView *scrollView = [[UIScrollView alloc] init]; UIImageView *imageView = [[UIImageView alloc] init]; [imageView setImage:[UIImage imageNamed:@"pic.jpg"]]; [self.view addSubview:scrollView]; [scrollView addSubview:imageView]; scrollView.translatesAutoresizingMaskIntoConstraints = NO; imageView.translatesAutoresizingMaskIntoConstraints = NO; self.imageViewPointer = imageView; // New self.scrollViewPointer = scrollView; scrollView.maximumZoomScale = 2; scrollView.minimumZoomScale = .5; scrollView.delegate = self; NSDictionary *viewsDictionary = NSDictionaryOfVariableBindings(scrollView, imageView); NSLog(@"Current views dictionary: %@", viewsDictionary); [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[scrollView]|" options:0 metrics: 0 views:viewsDictionary]]; [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[scrollView]|" options:0 metrics: 0 views:viewsDictionary]]; // Saving the image view width & height constraints self.imageViewConstraints = [[NSMutableArray alloc] init]; // Constrain the image view to be the same width & height of the scroll view [_imageViewConstraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[imageView(scrollView)]|" options:0 metrics: 0 views:viewsDictionary]]; [_imageViewConstraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[imageView(scrollView)]|" options:0 metrics: 0 views:viewsDictionary]]; // Add the image view constraints to the VIEW, not the scroll view [self.view addConstraints:_imageViewConstraints]; // Flag self.imageViewConstraintsNeedUpdating = YES; }

Para recapitular aquí, agrego todas las restricciones a self.view, guardando las restricciones establecidas en UIImageView en una propiedad NSMutableArray y estableciendo un indicador de que las restricciones de UIImageView necesitan actualización.

Estas restricciones iniciales en UIImageView funcionan para configurarlo para empezar. Será del mismo ancho y alto que UIScrollView. Sin embargo, esto NO nos permitirá ampliar la vista de la imagen. Lo mantendrá con el mismo ancho / alto que la vista de desplazamiento. No es lo que queremos Es por eso que estoy guardando las restricciones y configurando la bandera. Nos encargaremos de eso en un minuto.

Ahora, configure la vista para hacer zoom:

- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView { return self.imageViewPointer; }

Ok, entonces ahora necesitamos permitirnos hacer zoom. Estoy eliminando las restricciones iniciales de UIImageView y agregando algunas nuevas, esta vez restringiendo el ancho y alto de UIScrollView contentSize:

- (void)scrollViewWillBeginZooming:(UIScrollView *)scrollView withView:(UIView *)view { if(_imageViewConstraintsNeedUpdating) { // Remove the previous image view constraints [self.view removeConstraints:_imageViewConstraints]; // Replace them with new ones, this time constraining against the width & height of the scroll view''s content, not the scroll view itself NSDictionary *viewsDictionary = NSDictionaryOfVariableBindings(_scrollViewPointer, _imageViewPointer); [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[_imageViewPointer(width)]|" options:0 metrics:@{@"width" : @(_scrollViewPointer.contentSize.width)} views:viewsDictionary]]; [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[_imageViewPointer(height)]|" options:0 metrics:@{@"height" : @(_scrollViewPointer.contentSize.height)} views:viewsDictionary]]; self.imageViewConstraintsNeedUpdating = NO; } } @end

No podemos establecer las restricciones de esta manera en -viewDidLoad porque la imagen no se ha procesado en el UIImageView todavía, por lo que UIScrollView''s contentSize será {0,0}.

Esto me parece bastante raro, pero funciona, usa diseño automático puro, y no puedo encontrar una mejor manera de hacerlo. Me parece que Apple necesita proporcionar una mejor manera de ampliar el contenido en UIScrollView Y usar restricciones de diseño automático.


Estas soluciones funcionan un poco. Esto es lo que hice, sin necesidad de hacks o subclases, con esta configuración:

[view] [scrollView] [container] [imageView] [imageView2]

  1. En IB, scrollView arriba, adelante, abajo y detrás de scrollView para view .
  2. Enganche la parte superior, delantera, inferior y posterior del container a scrollView .
  3. Conecte el centro-x y el centro-y del container al centro-x y al centro-y de scrollView y scrollView como eliminar en el tiempo de construcción . Esto solo es necesario para silenciar las advertencias en IB.
  4. Implementar viewForZoomingInScrollView: en el controlador de vista, que debería ser el delegado de scrollView, y desde ese método devolver el container .
  5. Al configurar la imagen de imageView , determine la escala de zoom mínima (ya que en este momento se mostrará en el tamaño original) y configúrela:

    CGSize mySize = self.view.bounds.size; CGSize imgSize = _imageView.image.size; CGFloat scale = fminf(mySize.width / imgSize.width, mySize.height / imgSize.height); _scrollView.minimumZoomScale = scale; _scrollView.zoomScale = scale; _scrollView.maximumZoomScale = 4 * scale;

Esto funciona para mí, al configurar la imagen, se amplía la vista de desplazamiento para mostrar toda la imagen y permite acercarse a 4 veces el tamaño inicial.


Los problemas que ocurren es el cambio de ubicación de la vista de la imagen durante el proceso de acercamiento. La ubicación de origen de la vista de la imagen cambiará a ser un valor negativo durante el zoom. Creo que esta es la razón por la cual se produce el movimiento brusco. Además, una vez que se completa el zoom, la vista de imagen todavía está en la ubicación incorrecta, lo que significa que las volutas aparecerán desfasadas.

Si implementa -(void) scrollViewDidZoom:(UIScrollView *)scrollView y registra el marco de UIImageView durante este tiempo, puede ver cómo cambia su origen.

Terminé haciendo que las cosas funcionen mediante la implementación de una estrategia como this

Y, además, cambiar el marco de la ContentView mientras se hace zoom

-(void) scrollViewDidZoom:(UIScrollView *)scrollView { CGRect cFrame = self.contentView.frame; cFrame.origin = CGPointZero; self.contentView.frame = cFrame; }


Tuve el mismo problema cuando traté de implementar el zoom de un proyecto de storyboard utilizando solo scrollView.

Lo arreglé agregando un reconocedor de gestos de pellizco separado. Lo arrastré desde la caja de herramientas a mi escena. Luego lo conecté a una acción que llamé "doPinch" que implementa el zoom. Lo conecté a una toma de corriente que llamé "pinchRecognizer" para poder acceder a su propiedad de escala. Esto parece anular el zoom incorporado de scrollView y el salto desaparece. Tal vez no cometa el mismo error con los orígenes, o maneja eso con más gracia. Es muy poco trabajo en la parte superior del diseño en IB.

Tan pronto como introduzca el reconocedor de gestos de pellizco en la escena, necesitará los métodos action y viewForZoomingInScrollView. Se pierda y el zoom deja de funcionar.

El código en mi controlador de vista es este:

- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView { return self.zoomableImage; } - (IBAction)doPinch:(id)sender { NSLog(@"In the pinch action now with scale: %f", self.pinchRecognizer.scale); [scrollView setZoomScale:self.pinchRecognizer.scale animated:NO]; }

Esta implementación muy básica tiene un efecto secundario: cuando vuelves a una imagen ampliada y haces zoom un poco más, el valor de la escala es 1.0f para que salte a la escala original.

Puede resolver esto introduciendo una propiedad "currentScale" para rastrear la escala y establecer la escala de reconocimiento de gestos de pellizco cuando empiece a hacer zoom de nuevo. Debe usar la propiedad de estado del reconocedor de gestos:

- (IBAction)doPinch:(id)sender { NSLog(@"In the pinch action now with scale: %f", self.pinchRecognizer.scale); NSLog(@"Gesture recognizer state is: %d", self.pinchRecognizer.state); switch (self.pinchRecognizer.state) { case 1: NSLog(@"Zoom begins, with current scale set to: %f", self.currentScale); [self.pinchRecognizer setScale:self.currentScale]; break; case 3: NSLog(@"Zoom ends, with pinch recognizer scale set to: %f", self.pinchRecognizer.scale); self.currentScale = self.pinchRecognizer.scale; default: break; } [scrollView setZoomScale:self.pinchRecognizer.scale animated:NO]; }


Una vez más, al volver a leer las notas de la versión iOS SDK 6.0 encontré que:

Tenga en cuenta que puede hacer que una subvista 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 subárbol de la vista de desplazamiento, como la supervista 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á incrustada.

Y aplicando restricciones de la siguiente manera lo hago funcionar:

[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[scrollView]|" options:0 metrics: 0 views:viewsDictionary]]; [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[scrollView]|" options:0 metrics: 0 views:viewsDictionary]]; [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[imageView(width)]" options:0 metrics:@{@"width":@(self.imageViewPointer.image.size.width)} views:viewsDictionary]]; [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[imageView(height)]" options:0 metrics:@{@"height":@(self.imageViewPointer.image.size.height)} views:viewsDictionary]];