Skip to content

Instantly share code, notes, and snippets.

@TimOliver
Last active November 23, 2022 09:14
Show Gist options
  • Star 19 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save TimOliver/71be0a8048af4bd86ede to your computer and use it in GitHub Desktop.
Save TimOliver/71be0a8048af4bd86ede to your computer and use it in GitHub Desktop.
Zooming to a specific CGPoint inside a UIScrollView (2015 Edition)
@implementation UIScrollView (ZoomToPoint)
/**
Zooms into the specified point of the scroll view's zoomable content view at the supplied scaled.
(The zoomable content view is the view that is returned in `viewForZoomingInScrollView:`
@param zoomPoint - In terms of the scroll view's co-ordinate space, the point to zoom to
@param scale - A value between minimumZoomScale and maximumZoomScale in which to zoom to.
@param animated - Whether the transition is animated, or instant
*/
- (void)zoomToPoint:(CGPoint)zoomPoint withScale:(CGFloat)scale animated:(BOOL)animated
{
//Ensure scale is clamped to the scroll view's allowed zooming range
scale = MIN(scale, self.maximumZoomScale);
scale = MAX(scale, self.minimumZoomScale);
//`zoomToRect` works on the assumption that the input frame is in relation
//to the content view when zoomScale is 1.0
//Work out in the current zoomScale, where on the contentView we are zooming
CGPoint translatedZoomPoint = CGPointZero;
translatedZoomPoint.x = zoomPoint.x + self.contentOffset.x;
translatedZoomPoint.y = zoomPoint.y + self.contentOffset.y;
//Figure out what zoom scale we need to get back to default 1.0f
CGFloat zoomFactor = 1.0f / self.zoomScale;
//By multiplying by the zoom factor, we get where we're zooming to, at scale 1.0f;
translatedZoomPoint.x *= zoomFactor;
translatedZoomPoint.y *= zoomFactor;
//work out the size of the rect to zoom to, and place it with the zoom point in the middle
CGRect destinationRect = CGRectZero;
destinationRect.size.width = CGRectGetWidth(self.frame) / scale;
destinationRect.size.height = CGRectGetHeight(self.frame) / scale;
destinationRect.origin.x = translatedZoomPoint.x - (CGRectGetWidth(destinationRect) * 0.5f);
destinationRect.origin.y = translatedZoomPoint.y - (CGRectGetHeight(destinationRect) * 0.5f);
if (animated) {
[UIView animateWithDuration:0.55f delay:0.0f usingSpringWithDamping:1.0f initialSpringVelocity:0.6f options:UIViewAnimationOptionAllowUserInteraction animations:^{
[self zoomToRect:destinationRect animated:NO];
} completion:^(BOOL completed) {
if ([self.delegate respondsToSelector:@selector(scrollViewDidEndZooming:withView:atScale:)]) {
[self.delegate scrollViewDidEndZooming:self withView:[self.delegate viewForZoomingInScrollView:self] atScale:scale];
}
}];
}
else {
[self zoomToRect:destinationRect animated:NO];
}
}
@end
@umangbista
Copy link

How to implement double tap zoom using this code?

@ValentinFesenko
Copy link

Great job, Man!

@TimOliver
Copy link
Author

@umangbista Use a UITapGestureRecognizer. :)

@ValentinFesenko Thanks a lot man! :)

@hemang-azilen
Copy link

Hi, how can I scroll from 0, 0 position to particular points using above code?

@kmaschke85
Copy link

Thanks works really great!!!
Here a swift version of your code:

extension UIScrollView {
    func zoom(toPoint zoomPoint : CGPoint, scale : CGFloat, animated : Bool) {
        var scale = CGFloat.minimum(scale, maximumZoomScale)
        scale = CGFloat.maximum(scale, self.minimumZoomScale)
        
        var translatedZoomPoint : CGPoint = .zero
        translatedZoomPoint.x = zoomPoint.x + contentOffset.x
        translatedZoomPoint.y = zoomPoint.y + contentOffset.y
        
        let zoomFactor = 1.0 / zoomScale
        
        translatedZoomPoint.x *= zoomFactor
        translatedZoomPoint.y *= zoomFactor
        
        var destinationRect : CGRect = .zero
        destinationRect.size.width = frame.width / scale
        destinationRect.size.height = frame.height / scale
        destinationRect.origin.x = translatedZoomPoint.x - destinationRect.width * 0.5
        destinationRect.origin.y = translatedZoomPoint.y - destinationRect.height * 0.5
        
        if animated {
            UIView.animate(withDuration: 0.55, delay: 0.0, usingSpringWithDamping: 1.0, initialSpringVelocity: 0.6, options: [.allowUserInteraction], animations: {
                self.zoom(to: destinationRect, animated: false)
            }, completion: {
                completed in
                if let delegate = self.delegate, delegate.responds(to: #selector(UIScrollViewDelegate.scrollViewDidEndZooming(_:with:atScale:))), let view = delegate.viewForZooming?(in: self) {
                    delegate.scrollViewDidEndZooming!(self, with: view, atScale: scale)
                }
            })
        } else {
            zoom(to: destinationRect, animated: false)
        }
    }
}

@MKGitHub
Copy link

@kmaschke85 Thanks works great!

@01GOD
Copy link

01GOD commented May 22, 2021

@kmaschke85
Thanks, but it isn't animating. Happen to know why? Thanks in advance!

@01GOD
Copy link

01GOD commented Sep 15, 2022

@kmaschke85 Thanks for posting that, but it isn't animating for some reason. Happen to know how to solve that?
Thanks in advance for explaining!

@kmaschke85
Copy link

Sorry @01GOD can't tell. It's animating fine for me. Can you provide an example where it's not working for you?

@01GOD
Copy link

01GOD commented Sep 16, 2022

@kmaschke85 Seems there is an issue with the UIView animation system in that app not animating. I built a custom animator for that today.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment