40

I successfully implemented a pinch a zoom of a view. However, the view doesn't position itself where I wished it to be. For the stackoverflowers with an iPad, I would like my view to be centered like on the iPad Photos.app : when you pinch&zoom on an album, the photos present themselves in a view that is expanding. This view is approximately centered with the top right hand corner on the first finger and the bottom left hand finger on the other finger. I mixed it with a pan recognizer, but this way the user always has to pinch, and then pan to adjust.

Here are so graphic explanation, I could post a video of my app if that's unclear (no secret, i'm trying to reproduce the Photos.app of the iPad...)

So for an initial position of the fingers, begining zooming :

enter image description here

This is the actual "zoomed" frame for now. The square is bigger, but the position is below the fingers

given the start position

Here is what I would like to have : same size, but different origin.x and y :

enter image description here

(sorry about my poor photoshop skills ^^)

Thomas Joulin
  • 6,590
  • 9
  • 53
  • 88
  • HI Thomas, i'm also very interested about that. Did you find any solutions about it? Do you have any samples codes? thx~ – ludo Oct 11 '11 at 06:25
  • 2
    I haven't had the time to get back to this issue, but @md_develop answer looks great. I'll try to find some time to update my code and accept the answer if this works – Thomas Joulin Oct 11 '11 at 08:30

2 Answers2

44

You can get the CGPoint of the midpoint between two fingers via the following code in the method handlingPinchGesture.

CGPoint point = [sender locationInView:self];

My whole handlePinchGesture method is below.

/*
instance variables

CGFloat lastScale;
CGPoint lastPoint;
*/

- (void)handlePinchGesture:(UIPinchGestureRecognizer *)sender {
    if ([sender numberOfTouches] < 2)
        return;

    if (sender.state == UIGestureRecognizerStateBegan) {
        lastScale = 1.0;
        lastPoint = [sender locationInView:self];
    }

    // Scale
    CGFloat scale = 1.0 - (lastScale - sender.scale);
    [self.layer setAffineTransform:
        CGAffineTransformScale([self.layer affineTransform], 
                               scale, 
                               scale)];
    lastScale = sender.scale;

    // Translate
    CGPoint point = [sender locationInView:self];
    [self.layer setAffineTransform:
        CGAffineTransformTranslate([self.layer affineTransform], 
                                   point.x - lastPoint.x, 
                                   point.y - lastPoint.y)];
    lastPoint = [sender locationInView:self];
}
Benjamin Pollack
  • 27,594
  • 16
  • 81
  • 105
mishimay
  • 4,237
  • 1
  • 27
  • 23
  • 1
    how could this work when [sender locationInView:self] always returns the same point, the mid point between the two pinching fingers? – Guy Mar 25 '12 at 12:13
  • 1
    @Guy midpoint is changing after `UIGestureRecognizerStateBegan` because you move fingers away or closer. `locationInView:self` returns centroid between two fingers. That part do work. Although after 1 hour i still cant get this to work in my app, where i have bunch of layers that i need to move.. – Martin Berger Dec 10 '12 at 15:23
  • 1
    I used this method and the view is translated correctly. However the way it translated is not smooth like the Photo app – Tien Nguyen Aug 12 '13 at 08:01
  • @mishimay if I add a sublayer called `_myLayer` , and replace `self.layer` in your code to `_myLayer`, your code couldn't work correctly. why and how to fix it? thanks in advance. – Grey Aug 03 '17 at 06:19
11

Have a look at the Touches sample project. Specifically these methods could help you:

// scale and rotation transforms are applied relative to the layer's anchor point
// this method moves a gesture recognizer's view's anchor point between the user's fingers
- (void)adjustAnchorPointForGestureRecognizer:(UIGestureRecognizer *)gestureRecognizer {
    if (gestureRecognizer.state == UIGestureRecognizerStateBegan) {
        UIView *piece = gestureRecognizer.view;
        CGPoint locationInView = [gestureRecognizer locationInView:piece];
        CGPoint locationInSuperview = [gestureRecognizer locationInView:piece.superview];

        piece.layer.anchorPoint = CGPointMake(locationInView.x / piece.bounds.size.width, locationInView.y / piece.bounds.size.height);
        piece.center = locationInSuperview;
    }
}

// scale the piece by the current scale
// reset the gesture recognizer's rotation to 0 after applying so the next callback is a delta from the current scale
- (void)scalePiece:(UIPinchGestureRecognizer *)gestureRecognizer
{
    [self adjustAnchorPointForGestureRecognizer:gestureRecognizer];

    if ([gestureRecognizer state] == UIGestureRecognizerStateBegan || [gestureRecognizer state] == UIGestureRecognizerStateChanged) {
        [gestureRecognizer view].transform = CGAffineTransformScale([[gestureRecognizer view] transform], [gestureRecognizer scale], [gestureRecognizer scale]);
        [gestureRecognizer setScale:1];
    }
}
Felix
  • 35,354
  • 13
  • 96
  • 143