5

I want to zoom in and zoom out an image view and i dont want to use UIScrollView for that. so for this i used UIPinchGestureRecognizer and here is my code -

[recognizer view].transform = CGAffineTransformScale([[recognizer view] transform], [recognizer scale], [recognizer scale]);
recognizer.scale = 1;

this is working fine for zoom in and zoom out. But problem is that i want to zoom in and zoom out in specific scale like in UIScrollView we can set the maxZoom and minZoom. i could not found any solution for that, every tutorial about UIPinchGestureRecognizer just describe the same code.

TheTiger
  • 13,264
  • 3
  • 57
  • 82
  • You will have to write your own code. I did the same thing in one of my assignments, but I don't remember how well it works: https://github.com/nhahtdh/PS5/blob/master/Game/GameObject.mm#L295 I advise you to go over it again and refine it to your requirement. – nhahtdh Jun 09 '12 at 05:49
  • @nhahtdh - Thanks a lot for your answer.. but i am unable to find any solution yet :-( – TheTiger Jun 09 '12 at 06:53

4 Answers4

14

Declare 2 ivars CGFloat __scale and CGFloat __previousScale in the interface of the class that handles the gesture. Set __scale to 1.0 by overriding one of the init functions (make sure to call the super constructor here).

- (void)zoom:(UIPinchGestureRecognizer *)gesture { 
    NSLog(@"Scale: %f", [gesture scale]);

    if ([gesture state] == UIGestureRecognizerStateBegan) {
        __previousScale = __scale;
    }

    CGFloat currentScale = MAX(MIN([gesture scale] * __scale, MAX_SCALE), MIN_SCALE);  
    CGFloat scaleStep = currentScale / __previousScale;
    [self.view setTransform: CGAffineTransformScale(self.view.transform, scaleStep, scaleStep)];

    __previousScale = currentScale;

    if ([gesture state] == UIGestureRecognizerStateEnded || 
        [gesture state] == UIGestureRecognizerStateCancelled ||
        [gesture state] == UIGestureRecognizerStateFailed) {
        // Gesture can fail (or cancelled?) when the notification and the object is dragged simultaneously
        __scale = currentScale;
        NSLog(@"Final scale: %f", __scale);
    }
}
Sam Spencer
  • 8,492
  • 12
  • 76
  • 133
nhahtdh
  • 55,989
  • 15
  • 126
  • 162
  • I forgot to accept your answer... sorry for that but now i have accepted:-) – TheTiger Jul 24 '12 at 11:04
  • The problem I have with this approach is that you don't get immediate scale up/scale down if you overshoot your max bounds. How would you fix that? – Paul Solt Jun 14 '14 at 20:56
  • @PaulSolt: I'd like to help you with the problem, but I no longer have the environment to test. Would you please ask a new question instead? – nhahtdh Jun 15 '14 at 02:30
  • @PaulSolt: If you don't mind, could you show how my code is different from yours in a video? I compare with your code and I can't see why there would be a difference. – nhahtdh Jun 16 '14 at 06:35
  • You really need to run the code on a device to feel the problem. It's a lag problem that reduces the responsiveness when you expand past the limit, and then try to pinch back it. It won't start the shrinking until your fingers travel past the "limit" location. The difference with my code is that immediately as you pinch the view will shrink/expand. There's no invisible limit that you need to cross. – Paul Solt Jun 16 '14 at 13:39
  • @PaulSolt: Oh, I see what you meant. My code certainly will not shrink back if `[gesture scale]` causes the scale of the object to be larger than the maximum scale allowed. – nhahtdh Jun 16 '14 at 13:56
  • There is a lag when the object gets zoomed in and out. – Sneha Feb 15 '18 at 06:09
3
 – (void)handlePinchGesture:(UIPinchGestureRecognizer *)gestureRecognizer 
    {
        if([gestureRecognizer state] == UIGestureRecognizerStateBegan) {
        // Reset the last scale, necessary if there are multiple objects with different scales
        lastScale = [gestureRecognizer scale];
        }

        if ([gestureRecognizer state] == UIGestureRecognizerStateBegan ||
        [gestureRecognizer state] == UIGestureRecognizerStateChanged) {

        CGFloat currentScale = [[[gestureRecognizer view].layer valueForKeyPath:@”transform.scale”] floatValue];

        // Constants to adjust the max/min values of zoom
        const CGFloat kMaxScale = 2.0;
        const CGFloat kMinScale = 1.0;

        CGFloat newScale = 1 –  (lastScale – [gestureRecognizer scale]); // new scale is in the range (0-1)
        newScale = MIN(newScale, kMaxScale / currentScale);
        newScale = MAX(newScale, kMinScale / currentScale);
        CGAffineTransform transform = CGAffineTransformScale([[gestureRecognizer view] transform], newScale, newScale);
        [gestureRecognizer view].transform = transform;

        lastScale = [gestureRecognizer scale];  // Store the previous scale factor for the next pinch gesture call
        }

    }
Kaushik Movaliya
  • 799
  • 11
  • 27
  • Your code work for me thank u. But i have 1 doubt on this code `CGFloat currentScale = [[[gestureRecognizer view].layer valueForKeyPath:@”transform.scale”] floatValue];` I think apple will reject project with this code, because i read once that accessing member `valueForKeyPath:@”transform.scale”` Apple Reject. – hpDev_iOS Aug 25 '16 at 21:02
  • don't worry dear, i already use this code and upload on Appstore successfully before just 1 month. – Kaushik Movaliya Aug 26 '16 at 04:03
1

I had similar situation. My requirement was imageView will bounce back to its last transformation if imageView is smaller than a minimum size or bigger than a certain maximum size.

if ((self.frame.size.width > IMAGE_MIN_SIZE) && (self.frame.size.height > IMAGE_MIN_SIZE) && (self.frame.size.width < IMAGE_MAX_SIZE) && (self.frame.size.height < IMAGE_MAX_SIZE)) {
    lastSizeTransform = self.transform;
}else {
    self.transform = lastSizeTransform;
}

Here self is the imageView.

Warif Akhand Rishi
  • 23,920
  • 8
  • 80
  • 107
  • hi i have tried it but its not working - may be i'm confuse with lastSizeTransform and originalTransform ... i declare these variable like this CGAffineTransform lastSizeTransform; CGAffineTransform originalTransform; is it ok ?? – TheTiger Jun 09 '12 at 06:05
  • hello :-), Does your imageView frame size change after transformation? – Warif Akhand Rishi Jun 09 '12 at 06:10
  • ha ha - it is going to be minimum minimum minimum and in last its size have been 0 you can say invisible.. and all working is happen very fast just in 0.2-0.3 second – TheTiger Jun 09 '12 at 06:12
  • no need original transformation. First nslog to confirm your view frame does get smaller and bigger with the pinch gesture. than use this code... should work. – Warif Akhand Rishi Jun 09 '12 at 06:17
  • NSLog(@"%f",recognizer.scale); this is being change but my image view height and width remain as its original size... its only change imageView transform not height and width. – TheTiger Jun 09 '12 at 06:38
  • does NSLog(@"rect: %@", NSStringFromCGRect(recognizer.view.frame)); changes? – Warif Akhand Rishi Jun 09 '12 at 06:44
  • like this {{0, 0}, {317, 216}} {{-57.3616, 108}, {431.723, 0}} {{-1.2966e+37, 108}, {2.5932e+37, 0}} {{-376.158, 108}, {1065.32, 0}} {{-376.158, 108}, {1065.32, 0}} {{-376.158, 108}, {1065.32, 0}} – TheTiger Jun 09 '12 at 06:46
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/12329/discussion-between-warif-akhand-rishi-and-vakul-saini) – Warif Akhand Rishi Jun 09 '12 at 06:48
1

If you logged view.transform while you are pinching, you can see your image coordination which zoomed in and out. So, this solutions doesn't work as i expect. I made my solution like that;

Obj-C Version

    - (void)handlePinchGesture:(UIPinchGestureRecognizer *)recognizer {

        [recognizer.view setTransform:CGAffineTransformScale(recognizer.view.transform, 
recognizer.scale, recognizer.scale)];

        if (recognizer.view.transform.a > 1.6) {

            CGAffineTransform fooTransform = recognizer.view.transform;
            fooTransform.a = 1.6; // this is x coordinate
            fooTransform.d = 1.6; // this is y coordinate
            recognizer.view.transform = fooTransform;
        }

        if (recognizer.view.transform.a < 0.95) {

            CGAffineTransform fooTransform = recognizer.view.transform;
            fooTransform.a = 0.95; // this is x coordinate
            fooTransform.d = 0.95; // this is y coordinate
            recognizer.view.transform = fooTransform;
        }
        recognizer.scale = 1.0;
    }

Swift Version

func handlePinchGesture(recognizer: UIPinchGestureRecognizer) {

        if let view = recognizer.view {
            view.transform = CGAffineTransformScale(view.transform,
                recognizer.scale, recognizer.scale)
            if CGFloat(view.transform.a) > 1.6 {
                view.transform.a = 1.6 // this is x coordinate
                view.transform.d = 1.6 // this is x coordinate
            }
            if CGFloat(view.transform.d) < 0.95 {
                view.transform.a = 0.95 // this is x coordinate
                view.transform.d = 0.95 // this is x coordinate
            }
            recognizer.scale = 1
        }
    }
Kemal Can Kaynak
  • 1,638
  • 14
  • 26