ios uiscrollview zoom autolayout

ios - UIScrollView zooming con diseño automático



autolayout (3)

Estoy intentando implementar un UIScrollView the New Way , usando Auto Layout. He configurado restricciones desde la vista interna a la vista de desplazamiento para que pueda calcular su propio contentSize automáticamente, y eso funciona como un encanto, excepto que todo el infierno se desata cuando trato de acercarme o alejarme. Ni siquiera puedo describir adecuadamente lo que sucede, aparte de decir que la vista interior se "estropea".

Puede ver un ejemplo de este comportamiento here (no es mi proyecto; tiene que configurar la vista de desplazamiento maximumZoomScale e implementar -viewForZoomingInScrollView: antes de que funcione el zoom).

¿Alguien más ha tenido este comportamiento? ¿Existe actualmente alguna forma de hacer zoom en un UIScrollView para que funcione con Auto Layout sin volver a implementar el comportamiento del zoom?


Ejemplo completo de Swift Playground

El ejemplo más simple que se me ocurre es agregar un UIImageView a un UIScrollView . Esto es 100% en código, solo tendrá que agregar un PNG al patio de juegos. Llamé al mío Image.png . En un Patio de recreo, verá todo en una ''Vista en vivo''. Pinch-zoom funciona con Ctrl-clic para colocar un dedo en la pantalla y luego arrastrar. Hasta que el contenido se amplíe, el tamaño de la pantalla no funcionará. Toca dos veces la imagen para alternar entre la escala 1x y 3x.

Basado en la nota técnica TN2154 de Apple: UIScrollView y Autolayout

Gotcha

Encontrarás que todo es muy frustrante si tu contenido no es más grande que el tamaño de la pantalla. Si su contenido se ajusta completamente en la pantalla, nada ocurrirá. Es por eso que debes hacer zoom para trabajar también. Si quieres probarte a ti mismo que funciona, prueba con una imagen realmente grande (más grande que la ventana).

import UIKit import PlaygroundSupport enum TapToggle { case Regular, Large } class ScrollingViewController : UIViewController { var tapToggle: TapToggle = .Large var scrollView: UIScrollView? var imageView: UIImageView? override func viewDidLoad() { let image = UIImage(named: "Image") let imageView = UIImageView(image: image) imageView.translatesAutoresizingMaskIntoConstraints = false imageView.backgroundColor = .white imageView.isUserInteractionEnabled = true let scrollView = UIScrollView() scrollView.minimumZoomScale = 0.5 scrollView.maximumZoomScale = 10.0 scrollView.delegate = self scrollView.translatesAutoresizingMaskIntoConstraints = false scrollView.addSubview(imageView) let imageViewKey = "imageView" let imageViews = [imageViewKey: imageView] scrollView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[/(imageViewKey)]|", options: [], metrics: nil, views: imageViews)) scrollView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|[/(imageViewKey)]|", options: [], metrics: nil, views: imageViews)) self.imageView = imageView scrollView.backgroundColor = .white self.view.addSubview(scrollView) let scrollViewKey = "scrollView" let scrollViews = [scrollViewKey: scrollView] self.view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[/(scrollViewKey)]|", options: [], metrics: nil, views: scrollViews)) self.view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|[/(scrollViewKey)]|", options: [], metrics: nil, views: scrollViews)) self.scrollView = scrollView let tapGesture = UITapGestureRecognizer(target: self, action: #selector(didDoubleTap(sender:))) tapGesture.numberOfTapsRequired = 2 self.imageView?.addGestureRecognizer(tapGesture) } @objc public func didDoubleTap(sender: AnyObject) { switch self.tapToggle { case .Regular: self.scrollView?.zoomScale = 1.0 self.tapToggle = .Large case .Large: self.scrollView?.zoomScale = 3.0 self.tapToggle = .Regular } } } extension ScrollingViewController: UIScrollViewDelegate { func viewForZooming(in scrollView: UIScrollView) -> UIView? { return self.imageView } func scrollViewDidEndZooming(_ scrollView: UIScrollView, with view: UIView?, atScale scale: CGFloat) { print("/(scale)") } } PlaygroundPage.current.needsIndefiniteExecution = true PlaygroundPage.current.liveView = ScrollingViewController()


La mejor respuesta que he visto es la de Mark ( https://.com/users/1051919/mark-kryzhanouski ), publicada aquí: UIScrollView Zoom no funciona con Autolayout .

El quid de esto es que tiene que anclar la vista de imagen que está anidada en la vista de desplazamiento, al elemento primario de la vista de desplazamiento. A pesar de la guía en las notas de lanzamiento de iOS 6, no es intuitivo para mí qué vista está "flotando" sobre qué. En este caso, la vista de desplazamiento es solo una vista de una sola imagen.

Hice mucha experimentación con esto, con la esperanza de encontrar un enfoque de todo el IB y no encontré ninguno. Aún puede generar la jerarquía de vistas en IB, pero aún tiene que agregar restricciones programáticas. Puede eliminar algunas o todas las restricciones predeterminadas (principalmente solo para apaciguar las advertencias de conflicto de restricciones), pero siempre necesita el código de Mark para vincular la vista de la imagen al elemento principal de la vista de desplazamiento, el principal general de la vista de imagen.

Parece que debería ser más simple que esto, "simplemente debería funcionar" pero:

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


Sin agregar una imageView en el guión gráfico, he encontrado perfectamente los siguientes trabajos:

-(UIImageView *)imageView { if (!_imageView) _imageView = [[UIImageView alloc] initWithFrame:CGRectZero]; return _imageView; } - (void)viewDidLoad { [super viewDidLoad]; [self.scrollView addSubview:self.imageView]; // Set the min and max: self.scrollView.minimumZoomScale = 0.2; self.scrollView.maximumZoomScale = 5.0; self.scrollView.delegate = self; // Set the content: self.scrollView.zoomScale = 1.0; // reset zoomScale for new image self.scrollView.contentSize = CGSizeMake(image.size.width/2, image.size.height/2); self.imageView.frame = CGRectMake(0, 0, image.size.width/2, image.size.height/2); self.imageView.image = image; } -(UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView { return self.imageView; }