Max/Min Scale of Pinch Zoom in UIPinchGestureRecognizer - iPhone iOS

后端 未结 10 1063
[愿得一人]
[愿得一人] 2020-11-29 17:29

How would I be able to limit the scale of the UIPinchGestureRecognizer to a min and max level? The scale property below seems to be relative to the last known scale (the de

相关标签:
10条回答
  • 2020-11-29 18:06

    Thanks, really useful code snippet above clamping to a minimum and maximum scale.

    I found that when I flipped the view first using:

    CGAffineTransformScale(gestureRecognizer.view.transform, -1.0, 1.0); 
    

    it would cause a flicker when scaling the view.

    Let me know what you think but the solution for me was to update the code sample above, and if the view has been flipped (flag set via property) then invert the scale value:

    if ([gestureRecognizer state] == UIGestureRecognizerStateBegan || [gestureRecognizer     state] == UIGestureRecognizerStateChanged)
    {
        CGFloat currentScale = [[[gestureRecognizer view].layer valueForKeyPath:@"transform.scale"] floatValue];
    
        if(self.isFlipped) // (inverting)
        {
            currentScale *= -1;
        }
    
        CGFloat newScale = 1 -  (self.lastScale - [gestureRecognizer scale]);
    
        newScale = MIN(newScale, self.maximumScaleFactor / currentScale);
        newScale = MAX(newScale, self.minimumScaleFactor / currentScale);
    
        CGAffineTransform transform = CGAffineTransformScale([[gestureRecognizer view] transform], newScale, newScale);
        gestureRecognizer.view.transform = transform;
    
        self.lastScale = [gestureRecognizer scale];  // Store the previous scale factor for the next pinch gesture call
    
    0 讨论(0)
  • 2020-11-29 18:06

    Can you use a scroll view instead? Then you could use scrollView.minimumZoomScale and scrollView.maximumZoomScale

    0 讨论(0)
  • 2020-11-29 18:11

    The problem with most of the other answers is that they are trying to deal with scale as a linear value, when in fact it is non-linear due to the way UIPinchGestureRecognizer calculates its scale property based on the touch distance. When this isn't taken into account, the user must use more or less pinch distance to 'undo' the scaling applied by a previous pinch gesture.

    Consider: suppose transform.scale = 1.0 and I place my fingers 6cm apart on the screen, then pinch inwards to 3cm apart - the resulting gestureRecognizer.scale is 0.5, and 0.5-1.0 is -0.5, so transform.scale will become 1.0+(-0.5) = 0.5. Now, I lift my fingers, place them back down 3cm apart and pinch outwards to 6cm. The resulting gestureRecognizer.scale will be 2.0, and 2.0-1.0 is 1.0, so transform.scale will become 0.5+1.0 = 1.5. Not what I wanted to happen.

    The fix is to calculate the delta pinch scale as a proportion of its previous value. I place my fingers down 6cm apart, and pinch inwards to 3cm, so gestureRecognizer.scale is 0.5. 0.5/1.0 is 0.5, so my new transform.scale is 1.0*0.5 = 0.5. Next, I place my fingers down 3cm apart, and pinch outwards to 6cm. gestureRecognizer.scale is then 2.0, and 2.0/1.0 is 2.0, so my new transform.scale is 0.5*2.0 = 1.0, which is exactly what I wanted to happen.

    Here it is in code:

    in -(void)viewDidLoad:

    self.zoomGestureCurrentZoom = 1.0f;
    

    in -(void)onZoomGesture:(UIPinchGestureRecognizer*)gestureRecognizer:

    if ( gestureRecognizer.state == UIGestureRecognizerStateBegan )
    {
        self.zoomGestureLastScale = gestureRecognizer.scale;
    }
    else if ( gestureRecognizer.state == UIGestureRecognizerStateChanged )
    {
        // we have to jump through some hoops to clamp the scale in a way that makes the UX intuitive
        float scaleDeltaFactor = gestureRecognizer.scale/self.zoomGestureLastScale;
        float currentZoom = self.zoomGestureCurrentZoom;
        float newZoom = currentZoom * scaleDeltaFactor;
        // clamp
        float kMaxZoom = 4.0f;
        float kMinZoom = 0.5f;
        newZoom = MAX(kMinZoom,MIN(newZoom,kMaxZoom));    
        self.view.transform = CGAffineTransformScale([[gestureRecognizer view] transform], newZoom, newZoom);
    
        // store for next time
        self.zoomGestureCurrentZoom = newZoom;
        self.zoomGestureLastScale = gestureRecognizer.scale;
    }
    
    0 讨论(0)
  • 2020-11-29 18:13

    Other approaches mentioned here did not work for me, but taking a couple things from previous answers and (in my opinion) simplifying things, I've got this to work for me. effectiveScale is an ivar set to 1.0 in viewDidLoad.

    -(void)zoomScale:(UIPinchGestureRecognizer *)recognizer
    {
        if([recognizer state] == UIGestureRecognizerStateEnded) {
            // Reset last scale
            lastScale = 1.0;
            return;
        }
    
        if ([recognizer state] == UIGestureRecognizerStateBegan ||
        [recognizer state] == UIGestureRecognizerStateChanged) {
    
            CGFloat pinchscale = [recognizer scale];
            CGFloat scaleDiff = pinchscale - lastScale;
    
            if (scaleDiff < 0)
                scaleDiff *= 2; // speed up zoom-out
            else
                scaleDiff *= 0.7; // slow down zoom-in
    
            effectiveScale += scaleDiff;
            // Limit scale between 1 and 2
            effectiveScale = effectiveScale < 1 ? 1 : effectiveScale;
            effectiveScale = effectiveScale > 2 ? 2 : effectiveScale;
    
            // Handle transform in separate method using new effectiveScale    
            [self makeAndApplyAffineTransform];
            lastScale = pinchscale;
        }
    }
    
    0 讨论(0)
提交回复
热议问题