iOS Pinch Scale and Two Finger Rotate at same time

Every time pinch: is called, you just compute the transform based on the pinch recognizer’s scale. Every time pinchRotate: is called, you just compute the transform based on the rotation recognizer’s rotation. You never combine the scale and the rotation into one transform.

Here’s an approach. Give yourself one new instance variable, _activeRecognizers:

NSMutableSet *_activeRecognizers;

Initialize it in viewDidLoad:

_activeRecognizers = [NSMutableSet set];

Use one method as the action for both recognizers:

- (IBAction)handleGesture:(UIGestureRecognizer *)recognizer
{
    SMImage *selectedImage = [DataCenter sharedDataCenter].selectedImage;

    switch (recognizer.state) {
        case UIGestureRecognizerStateBegan:
            if (_activeRecognizers.count == 0)
                selectedImage.referenceTransform = selectedImage.transform;
            [_activeRecognizers addObject:recognizer];
            break;

        case UIGestureRecognizerStateEnded:
            selectedImage.referenceTransform = [self applyRecognizer:recognizer toTransform:selectedImage.referenceTransform];
            [_activeRecognizers removeObject:recognizer];
            break;

        case UIGestureRecognizerStateChanged: {
            CGAffineTransform transform = selectedImage.referenceTransform;
            for (UIGestureRecognizer *recognizer in _activeRecognizers)
                transform = [self applyRecognizer:recognizer toTransform:transform];
            selectedImage.transform = transform;
            break;
        }

        default:
            break;
    }
}

You’ll need this helper method:

- (CGAffineTransform)applyRecognizer:(UIGestureRecognizer *)recognizer toTransform:(CGAffineTransform)transform
{
    if ([recognizer respondsToSelector:@selector(rotation)])
        return CGAffineTransformRotate(transform, [(UIRotationGestureRecognizer *)recognizer rotation]);
    else if ([recognizer respondsToSelector:@selector(scale)]) {
        CGFloat scale = [(UIPinchGestureRecognizer *)recognizer scale];
        return CGAffineTransformScale(transform, scale, scale);
    }
    else
        return transform;
}

This works if you’re just allowing rotating and scaling. (I even tested it!)

If you want to add panning, use a separate action method and just adjust selectedImage.center. Trying to do panning with rotation and scaling using selectedImage.transform is much more complicated.

Leave a Comment