UIScrollView zoom avec layout automatique

J'essaie d'implémenter un UIScrollView la Nouvelle Voie , en utilisant Auto Layout. J'ai mis en place des contraintes de la vue interne à la vue contentSize afin qu'elle puisse calculer son propre contentSize automatiquement, et cela fonctionne comme un charme – sauf que tout l'enfer se déstring quand j'essaye de faire un zoom avant ou arrière. Je ne peux même pas décrire correctement ce qui se passe, sauf pour dire que la vision intérieure est «foirée».

Vous pouvez voir un exemple de ce comportement ici (pas mon projet, vous devez définir le maximumZoomScale la vue de maximumZoomScale et implémenter -viewForZoomingInScrollView: avant que le zoom fonctionne).

Quelqu'un d'autre a-t-il rencontré ce comportement? Existe-t-il actuellement un moyen de zoomer dans un UIScrollView pour travailler avec Auto Layout sans ré-implémenter le comportement de zoom?

La meilleure réponse que j'ai vue est celle de Mark ( https://stackoverflow.com/users/1051919/mark-kryzhanouski ), postée ici: UIScrollView Zoom ne fonctionne pas avec Autolayout .

Le point crucial est que vous devez ancrer la vue de l'image qui est nestede dans la vue de défilement, au parent de la vue de défilement. Malgré les conseils dans les notes de version iOS 6, il n'est pas intuitif pour moi quelle vue est "flottante" sur quoi. Dans ce cas, la vue défilante est juste une vue d'image unique.

J'ai fait beaucoup d'expérimentation avec ceci, espérant find une approche tout-IB et n'en ai trouvé aucune. Vous pouvez toujours générer la hiérarchie de vue dans IB, mais vous devez toujours append des contraintes par programme. Vous pouvez supprimer certaines ou toutes les contraintes par défaut (principalement pour apaiser les avertissements de conflits de contraintes), mais vous avez toujours besoin du code de Mark pour lier l'affichage de l'image au parent de la vue défilement, le grand parent de l'image.

Il semble que cela devrait être plus simple que cela – cela "devrait fonctionner" mais:

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

Sans append un imageView dans le storyboard, j'ai trouvé les œuvres suivantes parfaitement:

 -(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; } 

Exemple complet de terrain de jeu rapide

L'exemple le plus simple UIImageView je puisse penser est l'ajout d'une UIImageView à une UIScrollView . Ceci est 100% dans le code, vous aurez juste besoin d'append un PNG sur le terrain de jeu. J'ai appelé le mien Image.png . Dans un terrain de jeu, vous verrez le tout rendu dans la 'Live View'. Pincer le zoom en utilisant un Ctrl-clic pour placer un doigt sur l'écran, puis en le faisant glisser. Tant que le contenu n'est pas agrandi, l'agrandissement ne fonctionnera pas. Appuyez deux fois sur l'image pour basculer entre l'échelle 1x et 3x.

Basé sur la note technique TN2154 d'Apple: UIScrollView et Autolayout

Je t'ai eu

Vous findez l'set très frustrant si votre contenu n'est pas plus grand que la taille de l'écran. Si votre contenu correspond parfaitement à l'écran, rien ne se passera. C'est pourquoi vous devez get le zoom pour travailler aussi. Si vous voulez vous prouver que cela fonctionne, testez avec une très grande image (plus grande que la window).

 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: [], mesortingcs: nil, views: imageViews)) scrollView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|[\(imageViewKey)]|", options: [], mesortingcs: 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: [], mesortingcs: nil, views: scrollViews)) self.view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|[\(scrollViewKey)]|", options: [], mesortingcs: 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()