I am going through the Sample code of iPhone WWDC 2010 - 104 PhotoScroller App. It's working great with my project related images (PDF Page Images)
but I am struggling detect touches in the PhotoScroller App. The Zooming using multiple touches is handled by the ScrollVoiew. Now I want to Zoom In/out the photo on double Tap. The Touchesbegan method is being called in TilingView Class. Then I used [super touchesbegan: withevent:] and now the touches are in the ImageScrollView Class.
How to get these touch events in PhotoViewController. How to achieve the zoom in and zoom out on touch ?
Can anyone help in this Regard ?
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.
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];
}
}
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.
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];
}
}
来源:https://stackoverflow.com/questions/3967971/how-to-zoom-in-out-photo-on-double-tap-in-the-iphone-wwdc-2010-104-photoscroll