How to Zoom In/Out Photo on double Tap in the iPhone WWDC 2010 - 104 PhotoScroller

亡梦爱人 提交于 2019-11-28 15:54:02
geekinit

I researched several different web sites and I came up with the following...

Place this code into your viewDidLoad or viewWillAppear method:

//////////////////////////////
// Listen for Double Tap Zoom

UITapGestureRecognizer *doubleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleDoubleTap:)];

[doubleTap setNumberOfTapsRequired:2];

[self.scrollView addGestureRecognizer:doubleTap];

[doubleTap release];

Add this to your header file:

- (void)handleDoubleTap:(UIGestureRecognizer *)gestureRecognizer;

Add this to your implementation:

- (void)handleDoubleTap:(UIGestureRecognizer *)gestureRecognizer {  

    if(self.scrollView.zoomScale > self.scrollView.minimumZoomScale)
        [self.scrollView setZoomScale:self.scrollView.minimumZoomScale animated:YES]; 
    else 
        [self.scrollView setZoomScale:self.scrollView.maximumZoomScale animated:YES]; 

  }  

Currently this does not center upon the area where the user double tapped.

possen

My code, based upon some of the code at link "UIImageView does not zoom" This code handles toggling between zoomed in and zoomed out and will allow detection of a single tap along with a double tap. It also properly centers the zoom on the embedded image by applying the view transform on it. This code would go in the ImageScrollView class from the sample code.

- (void)setupGestureRecognisers:(UIView *)viewToAttach {

    UITapGestureRecognizer *dblRecognizer;
    dblRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self
                                                        action:@selector(handleDoubleTapFrom:)];
    [dblRecognizer setNumberOfTapsRequired:2];

    [viewToAttach addGestureRecognizer:dblRecognizer];
    self.doubleTapRecognizer = dblRecognizer;

    UITapGestureRecognizer *recognizer;
    recognizer = [[UITapGestureRecognizer alloc] initWithTarget:self
                                                     action:@selector(handleTapFrom:)];
    [recognizer requireGestureRecognizerToFail:dblRecognizer];

    [viewToAttach addGestureRecognizer:recognizer];
    self.tapRecognizer = recognizer;
}

- (void)handleTapFrom:(UITapGestureRecognizer *)recognizer {

   // do your single tap
}


- (CGRect)zoomRectForScale:(float)scale withCenter:(CGPoint)center {

    CGRect zoomRect;

    zoomRect.size.height = [_imageView frame].size.height / scale;
    zoomRect.size.width  = [_imageView frame].size.width  / scale;

    center = [_imageView convertPoint:center fromView:self];

    zoomRect.origin.x    = center.x - ((zoomRect.size.width / 2.0));
    zoomRect.origin.y    = center.y - ((zoomRect.size.height / 2.0));

    return zoomRect;
}

- (void)handleDoubleTapFrom:(UITapGestureRecognizer *)recognizer {

    float newScale = [self zoomScale] * 4.0;

    if (self.zoomScale > self.minimumZoomScale)
    {
        [self setZoomScale:self.minimumZoomScale animated:YES]; 
    }
    else 
    {
        CGRect zoomRect = [self zoomRectForScale:newScale 
                                   withCenter:[recognizer locationInView:recognizer.view]];
        [self zoomToRect:zoomRect animated:YES];
    }
}
n8tr

Here is a Swift solution based on @possen's great answer.
- Put this in your view controller that contains the scrollview and is the scrollview delegate.
- This solution is great since it actually zooms to the tap location:

@IBAction func handleDoubleTapScrollView(recognizer: UITapGestureRecognizer) {
    if scrollView.zoomScale == 1 {
        scrollView.zoom(to: zoomRectForScale(scale: scrollView.maximumZoomScale, center: recognizer.location(in: recognizer.view)), animated: true)
    } else {
        scrollView.setZoomScale(1, animated: true)
    }
}

func zoomRectForScale(scale: CGFloat, center: CGPoint) -> CGRect {
    var zoomRect = CGRect.zero
    zoomRect.size.height = imageView.frame.size.height / scale
    zoomRect.size.width  = imageView.frame.size.width  / scale
    let newCenter = scrollView.convert(center, from: imageView)
    zoomRect.origin.x = newCenter.x - (zoomRect.size.width / 2.0)
    zoomRect.origin.y = newCenter.y - (zoomRect.size.height / 2.0)
    return zoomRect
}

I combined the answers from @jayesh kavathiya and @possen into a single implementation which works pretty well, as long as you've set appropriate values for self.minimumZoomScale and self.maximumZoomScale.

- (void)doubleTap:(UITapGestureRecognizer*)recognizer
{
    if (self.zoomScale > self.minimumZoomScale)
    {
        [self setZoomScale:self.minimumZoomScale animated:YES];
    }
    else
    {
        CGPoint touch = [recognizer locationInView:recognizer.view];

        CGSize scrollViewSize = self.bounds.size;

        CGFloat w = scrollViewSize.width / self.maximumZoomScale;
        CGFloat h = scrollViewSize.height / self.maximumZoomScale;
        CGFloat x = touch.x-(w/2.0);
        CGFloat y = touch.y-(h/2.0);

        CGRect rectTozoom=CGRectMake(x, y, w, h);
        [self zoomToRect:rectTozoom animated:YES];
    }
}

Swift 3

Normally image zoom functionality is required with both double tap and pinch gesture, so I am providing complete solution to achieve the same. Double tap zoom is inspired by above answers and pinch zoom was taken from here.

import UIKit

class ViewController: UIViewController, UIScrollViewDelegate {

    var imageView: UIImageView!
    var scrollImg: UIScrollView!

    override func viewDidLoad() {
        super.viewDidLoad()

        let vWidth = self.view.frame.width
        let vHeight = self.view.frame.height

        scrollImg = UIScrollView()
        scrollImg.delegate = self
        scrollImg.frame = CGRect(x: 0, y: 0, width: vWidth, height: vHeight)
        scrollImg.backgroundColor = UIColor(red: 90, green: 90, blue: 90, alpha: 0.90)
        scrollImg.alwaysBounceVertical = false
        scrollImg.alwaysBounceHorizontal = false
        scrollImg.showsVerticalScrollIndicator = true
        scrollImg.flashScrollIndicators()

        scrollImg.minimumZoomScale = 1.0
        scrollImg.maximumZoomScale = 10.0

        let doubleTapGest = UITapGestureRecognizer(target: self, action: #selector(handleDoubleTapScrollView(recognizer:)))
        doubleTapGest.numberOfTapsRequired = 2
        scrollImg.addGestureRecognizer(doubleTapGest)

        self.view.addSubview(scrollImg)

        imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: vWidth, height: vHeight))
        imageView.image = UIImage(named: "cat")
        imageView!.layer.cornerRadius = 11.0
        imageView!.clipsToBounds = false
        scrollImg.addSubview(imageView!)
    }

    func handleDoubleTapScrollView(recognizer: UITapGestureRecognizer) {
        if scrollImg.zoomScale == 1 {
            scrollImg.zoom(to: zoomRectForScale(scale: scrollImg.maximumZoomScale, center: recognizer.location(in: recognizer.view)), animated: true)
        } else {
            scrollImg.setZoomScale(1, animated: true)
        }
    }

    func zoomRectForScale(scale: CGFloat, center: CGPoint) -> CGRect {
        var zoomRect = CGRect.zero
        zoomRect.size.height = imageView.frame.size.height / scale
        zoomRect.size.width  = imageView.frame.size.width  / scale
        let newCenter = imageView.convert(center, from: scrollImg)
        zoomRect.origin.x = newCenter.x - (zoomRect.size.width / 2.0)
        zoomRect.origin.y = newCenter.y - (zoomRect.size.height / 2.0)
        return zoomRect
    }

    func viewForZooming(in scrollView: UIScrollView) -> UIView? {
        return self.imageView
    }

}

use this code so it will zoom where you click.......

- (void)handleDoubleTap:(UIGestureRecognizer *)recognizer {  
        if(zoomCheck){
            CGPoint Pointview=[recognizer locationInView:self];
            CGFloat newZoomscal=3.0;

            newZoomscal=MIN(newZoomscal, self.maximumZoomScale);

            CGSize scrollViewSize=self.bounds.size;

            CGFloat w=scrollViewSize.width/newZoomscal;
            CGFloat h=scrollViewSize.height /newZoomscal;
            CGFloat x= Pointview.x-(w/2.0);
            CGFloat y = Pointview.y-(h/2.0);

            CGRect rectTozoom=CGRectMake(x, y, w, h);
            [self zoomToRect:rectTozoom animated:YES]; 

            [self setZoomScale:3.0 animated:YES]; 
            zoomCheck=NO;
        }
        else{ 
            [self setZoomScale:1.0 animated:YES]; 
            zoomCheck=YES;
        }
    } 

use this code so it will zoom where you click....... zoomChech is bool variable for checking zoom

Previous answer could be simpler with BlocksKit, like below.

#import "BlocksKit.h"
...
UITapGestureRecognizer *doubleTap = [[UITapGestureRecognizer alloc] initWithHandler:^(UIGestureRecognizer *sender, UIGestureRecognizerState state, CGPoint location) {
    if(scrollView.zoomScale != 1.0){
        [scrollView setZoomScale:1.0 animated:YES];
    }else{
        [scrollView setZoomScale:2.0 animated:YES];
    }
}];
[doubleTap setNumberOfTapsRequired:2];
[scrollView addGestureRecognizer:doubleTap];

How about adding a UITapGestureRecognizer to the ImageScrollView?

As long as you are looking at sample code, you can check out how UIGestureRecognizers are used in the ScrollViewSuite sample code.

Subclass the UIScrollView an in m file add

#pragma mark -
#pragma mark doubleTouch

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    UITouch *touch = [touches anyObject];
    NSUInteger tapCount = [touch tapCount];
    switch (tapCount) {
        case 2:
        {
            if(self.zoomScale <1.0){
                [self setZoomScale:1.0 animated:YES];
            }else{
                [self setZoomScale:0.1 animated:YES];
            }

            break;
        }
        default:
            break;
    }
}

This appears to work using convertRect:fromView: and zoomToRect:animated::

-(void)tap:(UITapGestureRecognizer *)tapGestureRecognizer
{
    CGFloat zoomeScaleMultiplier = 0.5;
    UIScrollView *scrollView = (UIScrollView *) tapGestureRecognizer.view;
    UIView *zoomableView = [scrollView.delegate viewForZoomingInScrollView:scrollView];

    CGRect rect = scrollView.bounds;
    rect.origin = CGPointZero;

    CGAffineTransform transform = CGAffineTransformMakeScale(zoomeScaleMultiplier, zoomeScaleMultiplier);
    rect = CGRectApplyAffineTransform(rect, transform);

    rect.origin = [tapGestureRecognizer locationInView:scrollView];

    rect = CGRectOffset(rect, CGRectGetWidth(rect)/-2., CGRectGetHeight(rect)/-2.);

    rect = [zoomableView convertRect:rect fromView:scrollView];

    [scrollView zoomToRect:rect animated:YES];
}

Zooms in on tap location

CGFloat scale = 3.0;
CGFloat scale = ScaleToAspectFitRectAroundRect(frame, self.bounds) * 2.0;

CGRect zoomRect;
CGPoint point = [gestureRecognizer locationOfTouch:0 inView:[gestureRecognizer view]];

zoomRect.size.height = frame.size.height * scale;
zoomRect.size.width  = frame.size.width  * scale;

zoomRect.origin.x = CGRectGetMidX(frame) - (CGRectGetWidth(zoomRect)/2) + (scale * (CGRectGetWidth(frame)/2 - point.x)) - (CGRectGetWidth(frame)/2 - point.x);
zoomRect.origin.y = CGRectGetMidY(frame) - (CGRectGetHeight(zoomRect)/2) + (scale * (CGRectGetHeight(frame)/2 - point.y)) - (CGRectGetHeight(frame)/2 - point.y);

You will need to implement viewForZooming(in scrollView: UIScrollView) -> UIView? in UIScrollViewDelegate in order to get @n8tr's answer working.

So the complete code will look something like this

class ViewController: UIViewController, UIScrollViewDelegate {

    @IBOutlet weak var scrollView: UIScrollView!
    @IBOutlet weak var imageView: UIImageView!


    override func viewDidLoad() {
        super.viewDidLoad()

        scrollView.delegate = self
    }

    @IBAction func handleDoubleTapScrollView(recognizer: UITapGestureRecognizer) {
        if scrollView.zoomScale == 1 {
            scrollView.zoom(to: zoomRectForScale(scale: scrollView.maximumZoomScale, center: recognizer.location(in: recognizer.view)), animated: true)
        } else {
            scrollView.setZoomScale(1, animated: true)
        }
    }

    func zoomRectForScale(scale: CGFloat, center: CGPoint) -> CGRect {
        var zoomRect = CGRect.zero
        zoomRect.size.height = imageView.frame.size.height / scale
        zoomRect.size.width  = imageView.frame.size.width  / scale
        let newCenter = imageView.convert(center, from: scrollView)
        zoomRect.origin.x = newCenter.x - (zoomRect.size.width / 2.0)
        zoomRect.origin.y = newCenter.y - (zoomRect.size.height / 2.0)
        return zoomRect
    }


    func viewForZooming(in scrollView: UIScrollView) -> UIView? {
        return imageView
    }
}

It is the latest code with swift 5 for Pinch zoom and double tap zoom.

import UIKit

class ViewController: UIViewController, UIScrollViewDelegate {

@IBOutlet weak var scrollView: UIScrollView!
@IBOutlet weak var imageView: UIImageView!

override func viewDidLoad() {
    super.viewDidLoad()
    scrollView.delegate = self

    let doubleTapGest = UITapGestureRecognizer(target: self, action: #selector(handleDoubleTapScrollView(recognizer:)))
    doubleTapGest.numberOfTapsRequired = 2
    scrollView.addGestureRecognizer(doubleTapGest)
}

func viewForZooming(in scrollView: UIScrollView) -> UIView? {
    return imageView
}

@objc func handleDoubleTapScrollView(recognizer: UITapGestureRecognizer) {
    if scrollView.zoomScale == 1 {
        scrollView.zoom(to: zoomRectForScale(scale: scrollView.maximumZoomScale, center: recognizer.location(in: recognizer.view)), animated: true)
    }
    else {
        scrollView.setZoomScale(1, animated: true)
    }
}

func zoomRectForScale(scale: CGFloat, center: CGPoint) -> CGRect {
    var zoomRect = CGRect.zero
    zoomRect.size.height = imageView.frame.size.height / scale
    zoomRect.size.width  = imageView.frame.size.width  / scale
    let newCenter = imageView.convert(center, from: scrollView)
    zoomRect.origin.x = newCenter.x - (zoomRect.size.width / 2.0)
    zoomRect.origin.y = newCenter.y - (zoomRect.size.height / 2.0)
    return zoomRect
}


}

I attached storyboard picture too.

Ha cong Thuan

here my code work for me it's simple :

// Listen for Double Tap Zoom
UITapGestureRecognizer *doubleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleDoubleTap:)];

[doubleTap setNumberOfTapsRequired:2];

[self addGestureRecognizer:doubleTap];

you need

BOOL checkZoomImage;

Here tab method

- (void)handleDoubleTap:(UIGestureRecognizer *)gestureRecognizer {

    if (self.zoomScale > self.minimumZoomScale && self.zoomScale == self.maximumZoomScale){
        checkZoomImage = YES;
    }
    if (self.zoomScale < self.maximumZoomScale && self.zoomScale == self.minimumZoomScale) {
        checkZoomImage = NO;
    }


    if (checkZoomImage) {
        [self setZoomScale:self.zoomScale *0.5 animated:YES];
    }else{
        [self setZoomScale:self.zoomScale *1.5 animated:YES];
    }

}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!