UIScrollView Zoom Does Not Work With Autolayout
Once again, re-reading the iOS SDK 6.0 release notes I found that:
Note that you can make a subview of the scroll view appear to float (not scroll) over the other scrolling content by creating constraints between the view and a view outside the scroll view’s subtree, such as the scroll view’s superview.
Solution
Connect your subview to the outer view. In another words, to the view in which scrollview is embedded.
And applying constraints in following way I've got it work:
[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]];
The issues that occurs is the changing of location of the imageview during the zoom process. The origin location of the imageview will change to be a negative value during the zoom. I believe this is why the jerky movement occurs. As well, after the zoom is complete the imageView is still in the wrong location meaning that scrolls will appear to be offset.
If you implement -(void) scrollViewDidZoom:(UIScrollView *)scrollView
and log the frame of the UIImageView during this time you can see its origin changing.
I ended up making things work out by implementing a strategy like this
And in addition changing the frame of the contentView while zooming
-(void) scrollViewDidZoom:(UIScrollView *)scrollView { CGRect cFrame = self.contentView.frame; cFrame.origin = CGPointZero; self.contentView.frame = cFrame;}
These solutions all kinda work. Here is what I did, no hacks or subclasses required, with this setup:
[view] [scrollView] [container] [imageView] [imageView2]
- In IB, hook up top, leading, bottom and trailing of
scrollView
toview
. - Hook up top, leading, bottom and trailing of
container
toscrollView
. - Hook up center-x and center-y of
container
to center-x and center-y ofscrollView
and mark it as remove on build time. This is only needed to silence the warnings in IB. - Implement
viewForZoomingInScrollView:
in the view controller, which should be scrollView's delegate, and from that method returncontainer
. When setting the
imageView
's image, determine minimum zoom scale (as right now it will be displayed at the native size) and set it: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;
This works for me, upon setting the image zooms the scroll view to show the entire image and allows to zoom in to 4x the initial size.