4

I can get pinch/zoom functionality working like this:

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

    if([recognizer state] == UIGestureRecognizerStateBegan) {
        _lastScale = 1.0;
    }

    CGFloat scale = 1.0 - (_lastScale - [recognizer scale]);

    CGAffineTransform currentTransform = self.imageForEditing.transform;
    CGAffineTransform newTransform = CGAffineTransformScale(currentTransform, scale, scale);

    [self.imageForEditing setTransform:newTransform];

    _lastScale = [recognizer scale];


}

The problem is that the image zooms based on it's center, so if you first pan the image so that it's not centered, then pinch/zoom, it doesn't zoom into the area between the touches. I figure this must be a common issue that has been addressed and solved but I haven't been able to find a solution.

soleil
  • 12,133
  • 33
  • 112
  • 183

3 Answers3

3
@objc private func pinchHandler(gesture: UIPinchGestureRecognizer) {
    if let view = gesture.view {

        switch gesture.state {
        case .changed:
            let pinchCenter = CGPoint(x: gesture.location(in: view).x - view.bounds.midX,
                                      y: gesture.location(in: view).y - view.bounds.midY)
            let transform = view.transform.translatedBy(x: pinchCenter.x, y: pinchCenter.y)
                                            .scaledBy(x: gesture.scale, y: gesture.scale)
                                            .translatedBy(x: -pinchCenter.x, y: -pinchCenter.y)
            view.transform = transform
            gesture.scale = 1
        case .ended:
            // Nice animation to scale down when releasing the pinch.
            // OPTIONAL
            UIView.animate(withDuration: 0.2, animations: {
                view.transform = CGAffineTransform.identity
            })
        default:
            return
        }


    }
}
Bruno Paulino
  • 5,611
  • 1
  • 41
  • 40
1

You can set the anchor point of the layer to be the 'center' of the two pinch-touches:

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

    if([recognizer state] == UIGestureRecognizerStateBegan) {
        _lastScale = 1.0;
        if ([recognizer numberOfTouches] >= 2) { //should always be true when using a PinchGR
            CGPoint touch1 = [recognizer locationOfTouch:0 inView:self.imageForEditing];
            CGPoint touch2 = [recognizer locationOfTouch:1 inView:self.imageForEditing];
            CGPoint mid;
            mid.x = ((touch2.x - touch1.x) / 2) + touch1.x;
            mid.y = ((touch2.y - touch1.y) / 2) + touch1.y;
            CGSize imageViewSize = self.imageForEditing.frame.size;
            CGPoint anchor;
            anchor.x = mid.x / imageViewSize.width;
            anchor.y = mid.y / imageViewSize.height;
            self.imageForEditing.layer.anchorPoint = anchor;
        }
    }

    CGFloat scale = 1.0 - (_lastScale - [recognizer scale]);

    CGAffineTransform currentTransform = self.imageForEditing.transform;
    CGAffineTransform newTransform = CGAffineTransformScale(currentTransform, scale, scale);

    [self.imageForEditing setTransform:newTransform];

    _lastScale = [recognizer scale];


}

That way the image should scale relative to the center between the two touches.

For further details see the Core Animation Programming Guide - Core Animation Basics

Tobi
  • 5,499
  • 3
  • 31
  • 47
  • I see the theory here, but in practice it's not quite working for me. imageViewSize keeps getting larger as you zoom in, so anchor just gets smaller and smaller and tends toward (0,0), no matter which area of the image you're trying to zoom into. – soleil Apr 20 '13 at 16:25
  • Does the calculation of "mid" need to take into account the scale/rotate transform of the view? – soleil Apr 20 '13 at 16:38
  • You don't need to calculate mid. Just use -locationInView: which returns the center of the gesture no matter how many points are used. It also simplifies the code. – Victor Engel Jun 04 '23 at 20:36
0

Try out the code below to zoom In/Out and translate the centre of a view using Pinch Gesture Recognizer. Put this piece of code in your UIGestureRecognizerStateBegan:

        previousTouchPoint = [pinchRecognizer locationInView:self.view];

and this in your UIGestureRecognizerStateChanged:

       //zoom in/out view based on scale
        CGFloat zoomScale = pinchRecognizer.scale;
        CGRect tRect = pinchedView.bounds;
        tRect.size.width = pinchedView.frame.size.width * zoomScale;
        tRect.size.height = pinchedView.frame.size.height * zoomScale;
        pinchedView.bounds = tRect;

        //sets the center of view
        CGPoint currentTouchPoint = [pinchRecognizer locationInView:self.view];
        CGPoint tCenter = pinchedView.center;
        tCenter.x -= (previousTouchPoint.x - currentTouchPoint.x);
        tCenter.y -= (previousTouchPoint.y - currentTouchPoint.y);
        pinchedView.center = tCenter;
        previousTouchPoint = [pinchRecognizer locationInView:self.view];
Avinash
  • 4,304
  • 1
  • 23
  • 18