UIPinchGestureRecognizer中捏放大的最大/最小缩放比例 - iPhone iOS

UIPinchGestureRecognizer中捏放大的最大/最小缩放比例 - iPhone iOS

问题描述:

如何才能将UIPinchGestureRecognizer的缩放比例限制为最小和最大值?下面的缩放属性似乎是相对于最后一个已知的缩放比例(来自上一个状态的增量),我无法弄清楚如何设置对缩放对象的大小/高度的限制。UIPinchGestureRecognizer中捏放大的最大/最小缩放比例 - iPhone iOS

-(void)scale:(id)sender { 

[self.view bringSubviewToFront:[(UIPinchGestureRecognizer*)sender view]]; 

if([(UIPinchGestureRecognizer*)sender state] == UIGestureRecognizerStateEnded) { 
    lastScale = 1.0; 
    return; 
} 

CGFloat pinchscale = [(UIPinchGestureRecognizer*)sender scale]; 
CGFloat scale = 1.0 - (lastScale - pinchscale); 
CGAffineTransform currentTransform = [(UIPinchGestureRecognizer*)sender view].transform; 
CGAffineTransform holderTransform = holderView.transform; 
CGAffineTransform newTransform = CGAffineTransformScale(currentTransform, scale, scale); 
[[(UIPinchGestureRecognizer*)sender view] setTransform:newTransform]; 

lastScale = [(UIPinchGestureRecognizer*)sender scale]; 

}

+0

看看这个样本上的双指缩放的http:// cocoabugs .blogspot.com/2011/03 /捏变焦使用,uipinchg estmmrecogniz.html – 2011-03-10 02:46:32

没有办法限制UIPinchGestureRecognizer上的比例。要限制在你的代码的高度,你应该能够做这样的事情:

CGFloat scale = 1.0 - (lastScale - pinchscale); 
CGRect bounds = [(UIPinchGestureRecognizer*)sender view].bounds; 
scale = MIN(scale, maximumHeight/CGRectGetHeight(bounds)); 
scale = MAX(scale, minimumHeight/CGRectGetHeight(bounds)); 

要在最后两行限制宽度,改变“高”到“宽”。

+0

哇,这正是我需要的! – VinnyD 2011-03-01 17:13:36

+0

这是不正确的。如果您完全转换视图,则该框架无效。例如,如果视图以45度旋转,则框架实际上比实际视图大得多,所以缩放因子会随着旋转而变化,然后尝试将其夹紧。 – 2011-03-27 00:04:36

+0

@PaulSolt:好点,但很容易修复。 – Anomie 2011-03-27 00:08:32

你能使用滚动视图?然后,您可以使用scrollView.minimumZoomScale和scrollView.maximumZoomScale

+0

嗯,有趣的...将作为解决问题的替代方法。 – VinnyD 2011-03-01 17:14:20

下面是我使用Anomie的答案作为出发点后想到的解决方案。

- (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]); 
     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 
    } 
} 
+2

伟大的解决方案!我一直在寻找这个永远!为什么它不在这个问题的顶部!?谢谢 – tentmaking 2013-05-31 00:10:35

+2

谢谢! - 谢谢 ! - 谢谢! 。 。 。 。:D – 2013-08-20 14:05:27

+1

在设置了gestureRecognizer视图的变换之后添加下面这行:[recognitionzer setScale:1.0]; – morph85 2013-11-13 10:07:59

我花了一些信息,从保罗绍尔特和Anoime的回答收集,并补充说,现有的类别我已经为UIViewController中允许进行任何的UIView拖动的,到现在为止使其pinchable用手势和变换。

注意:这使您可拖动/可捏合的视图的标签属性变脏。所以如果你需要标签来替代别的东西,你可以考虑把这个值放在这个技术使用的NSMutableDictionary中。这可作为[自dictForView:theView]

在项目实施:

可以使视图控制器内的任意子视图“视图”拖动或pinchable(或两者) 地方单行的代码在你的viewDidLoad中(例如:)

[self makeView:mySubView draggable:YES pinchable:YES minPinchScale:0.75 maxPinchScale:1.0]; 

关闭它在viewDidUnload(版本guestures &字典):

[self makeView:mySubView draggable:NO pinchable:NO minPinchScale:1.0 maxPinchScale:1.0]; 

DragAndPinchScale.h文件

#import <UIKit/UIKit.h> 

@interface UIViewController (DragAndPinchScale) 

-(void) makeView:(UIView*)aView 
     draggable:(BOOL)draggable 
     pinchable:(BOOL)pinchable 
    minPinchScale:(CGFloat)minPinchScale 
    maxPinchScale:(CGFloat)maxPinchScale; 


-(NSMutableDictionary *) dictForView:(UIView *)theView; 
-(NSMutableDictionary *) dictForViewGuestures:(UIGestureRecognizer *)guesture; 

@end 

DragAndPinchScale.m文件

#import "DragAndPinchScale.h" 

@implementation UIViewController (DragAndPinchScale) 


-(NSMutableDictionary *) dictForView:(UIView *)theView{ 
    NSMutableDictionary *dict = (NSMutableDictionary*) (void*) theView.tag; 
    if (!dict) { 
     dict = [[NSMutableDictionary dictionary ] retain]; 
     theView.tag = (NSInteger) (void *) dict; 
    } 

    return dict; 

} 


-(NSMutableDictionary *) dictForViewGuestures:(UIGestureRecognizer *)guesture { 
    return [self dictForView:guesture.view]; 
} 


- (IBAction)fingersDidPinchInPinchableView:(UIPinchGestureRecognizer *)fingers { 
    NSMutableDictionary *dict = [self dictForViewGuestures:fingers]; 
    UIView *viewToZoom = fingers.view; 
    CGFloat lastScale; 
    if([fingers state] == UIGestureRecognizerStateBegan) { 
     // Reset the last scale, necessary if there are multiple objects with different scales 
     lastScale = [fingers scale]; 
    } else { 
     lastScale = [[dict objectForKey:@"lastScale"] floatValue]; 
    } 

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

     CGFloat currentScale = [[[fingers view].layer valueForKeyPath:@"transform.scale"] floatValue]; 

     // limits to adjust the max/min values of zoom 
     CGFloat maxScale = [[dict objectForKey:@"maxScale"] floatValue]; 
     CGFloat minScale = [[dict objectForKey:@"minScale"] floatValue]; 

     CGFloat newScale = 1 - (lastScale - [fingers scale]); 
     newScale = MIN(newScale, maxScale/currentScale); 
     newScale = MAX(newScale, minScale/currentScale); 
     CGAffineTransform transform = CGAffineTransformScale([[fingers view] transform], newScale, newScale); 
     viewToZoom.transform = transform; 

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

    [dict setObject:[NSNumber numberWithFloat:lastScale] 
      forKey:@"lastScale"]; 

} 

- (void)fingerDidMoveInDraggableView:(UIPanGestureRecognizer *)finger { 
    NSMutableDictionary *dict = [self dictForViewGuestures:finger]; 
    UIView *viewToDrag = finger.view; 
    if (finger.state == UIGestureRecognizerStateBegan) { 

     [dict setObject:[NSValue valueWithCGPoint:viewToDrag.frame.origin] 
       forKey:@"startDragOffset"]; 

     [dict setObject:[NSValue valueWithCGPoint:[finger locationInView:self.view]] 
       forKey:@"startDragLocation"]; 


    } 
    else if (finger.state == UIGestureRecognizerStateChanged) { 

     NSMutableDictionary *dict = (NSMutableDictionary*) (void*) viewToDrag.tag; 

     CGPoint stopLocation = [finger locationInView:self.view]; 
     CGPoint startDragLocation = [[dict valueForKey:@"startDragLocation"] CGPointValue]; 
     CGPoint startDragOffset = [[dict valueForKey:@"startDragOffset"] CGPointValue]; 
     CGFloat dx = stopLocation.x - startDragLocation.x; 
     CGFloat dy = stopLocation.y - startDragLocation.y; 
     // CGFloat distance = sqrt(dx*dx + dy*dy); 
     CGRect dragFrame = viewToDrag.frame; 


     CGSize selfViewSize = self.view.frame.size; 
     if (!UIDeviceOrientationIsPortrait(self.interfaceOrientation)) { 
      selfViewSize = CGSizeMake(selfViewSize.height,selfViewSize.width); 
     } 

     selfViewSize.width -= dragFrame.size.width; 
     selfViewSize.height -= dragFrame.size.height; 

     dragFrame.origin.x = MIN(selfViewSize.width, MAX(0,startDragOffset.x+dx)); 
     dragFrame.origin.y = MIN(selfViewSize.height,MAX(0,startDragOffset.y+dy)); 

     viewToDrag.frame = dragFrame; 
    } 
    else if (finger.state == UIGestureRecognizerStateEnded) { 

     [dict removeObjectForKey:@"startDragLocation"]; 
     [dict removeObjectForKey:@"startDragOffset"]; 
    } 
} 

-(void) makeView:(UIView*)aView 
     draggable:(BOOL)draggable 
     pinchable:(BOOL)pinchable 
    minPinchScale:(CGFloat)minPinchScale 
    maxPinchScale:(CGFloat)maxPinchScale{ 
    NSMutableDictionary *dict = (NSMutableDictionary*) (void*) aView.tag; 

    if (!(pinchable || draggable)) { 

     if (dict){ 
      [dict release]; 
      aView.tag = 0; 
     } 
     return; 
    } 

    if (dict) { 

     UIPanGestureRecognizer *pan =[dict objectForKey:@"UIPanGestureRecognizer"]; 
     if(pan){ 
      if ([aView.gestureRecognizers indexOfObject:pan]!=NSNotFound) { 
       [aView removeGestureRecognizer:pan]; 
      } 
      [dict removeObjectForKey:@"UIPanGestureRecognizer"]; 
     } 

     UIPinchGestureRecognizer *pinch =[dict objectForKey:@"UIPinchGestureRecognizer"]; 
     if(pinch){ 
      if ([aView.gestureRecognizers indexOfObject:pinch]!=NSNotFound) { 
       [aView removeGestureRecognizer:pinch]; 
      } 
      [dict removeObjectForKey:@"UIPinchGestureRecognizer"]; 
     } 

     [dict removeObjectForKey:@"startDragLocation"]; 
     [dict removeObjectForKey:@"startDragOffset"]; 
     [dict removeObjectForKey:@"lastScale"]; 
     [dict removeObjectForKey:@"minScale"]; 
     [dict removeObjectForKey:@"maxScale"]; 
    } 


    if (draggable) { 

     UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(fingerDidMoveInDraggableView:)]; 
     pan.minimumNumberOfTouches = 1; 
     pan.maximumNumberOfTouches = 1; 
     [aView addGestureRecognizer:pan]; 
     [pan release]; 

     dict = [self dictForViewGuestures:pan]; 
     [dict setObject:pan forKey:@"UIPanGestureRecognizer"]; 

    } 

    if (pinchable) { 


     CGAffineTransform initialTramsform = CGAffineTransformMakeScale(1.0, 1.0); 
     aView.transform = initialTramsform; 


     UIPinchGestureRecognizer *pinch = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(fingersDidPinchInPinchableView:)]; 
     [aView addGestureRecognizer:pinch]; 
     [pinch release]; 
     dict = [self dictForViewGuestures:pinch]; 
     [dict setObject:pinch forKey:@"UIPinchGestureRecognizer"]; 
     [dict setObject:[NSNumber numberWithFloat:minPinchScale] forKey:@"minScale"]; 
     [dict setObject:[NSNumber numberWithFloat:maxPinchScale] forKey:@"maxScale"]; 


    } 

} 

@end 
+0

感谢它与我的代码工作正常。 我长期面对这个问题,但您的上述代码帮助我解决了这个问题。 再次感谢 – Prashant 2012-02-18 05:41:19

谢谢,真正有用的代码片段上面夹紧到最小和最大规模。

我发现,当我第一次翻转使用视图:

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

它将缩放视图时会闪烁。

让我知道你在想什么,但对我来说,解决方案是更新上面的代码示例,如果视图已经被翻转(旗通过属性设置),然后倒置刻度值:

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 

其他这里提到的方法并不适用于我,但从以前的答案和(在我看来)简化了一些事情,我已经得到了这个为我工作。 是在viewDidLoad中设置为1.0的伊娃。

-(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

这是一种处理手势识别器的“scale”属性中固有的非线性的复杂方式,它也没有正确处理它,它只是通过0.7来进行缩放。看到我的答案更好的方法:) – damian 2014-01-20 10:39:51

与大多数其他的答案的问题是,他们正试图处理规模为线性值,而实际上它是非线性的,由于道路UIPinchGestureRecognizer计算其规模属性基于触摸距离。当没有考虑到这一点时,用户必须使用或多或少的夹点距离来“撤消”前一个捏合手势所应用的缩放。

考虑:假设transform.scale = 1.0,我6厘米除了在屏幕上把我的手指,然后向内捏3厘米分开 - 生成的gestureRecognizer.scale0.5,并0.5-1.0-0.5,所以transform.scale将成为1.0+(-0.5) = 0.5。现在,我抬起我的手指,将它们放回距离3厘米,并向外捏6厘米。由此产生的gestureRecognizer.scale将是2.0,并且2.0-1.01.0,因此transform.scale将变为0.5+1.0 = 1.5。不是我想要发生的事情。

解决的办法是计算三角箍缩比例作为其先前值的比例。我将手指放在距离6厘米的地方,并向内捏至3厘米,所以gestureRecognizer.scale0.50.5/1.00.5,所以我的新transform.scale1.0*0.5 = 0.5。接下来,我将手指放下3厘米,然后向外掐6厘米。 gestureRecognizer.scale然后是2.02.0/1.02.0,所以我的新transform.scale0.5*2.0 = 1.0,这正是我想要发生的。

这是代码:

-(void)viewDidLoad

self.zoomGestureCurrentZoom = 1.0f; 

-(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

这不适合我当我将它设置为一个特定的视图,gestureRecognizer.view 我遇到了“速度”不同的多个pinches问题,我想这就是你所得到的。 – 2014-06-14 20:59:05

+0

@PaulSolt嗯,是的..我猜我在哪里'[[gestureRecognizer视图]转换]'也许可以通过'CGAffineTransformIdentity'?我认为在我基于此的代码中,'self.view'应该与'gestureRecognizer.view'不同。不记得对不起:/ – damian 2014-06-16 09:30:19

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

    //recognizer.scale=1; 

    CGFloat pinchScale = recognizer.scale; 
    pinchScale = round(pinchScale * 1000)/1000.0; 
    NSLog(@"%lf",pinchScale); 

if (pinchScale < 1) 

{ 

currentLabel.font = [UIFont fontWithName:currentLabel.font.fontName size: 

(currentLabel.font.pointSize - pinchScale)]; 

    recognizer.view.transform = CGAffineTransformScale(recognizer.view.transform, recognizer.scale, recognizer.scale); 

[currentLabel sizeToFit]; 

    recognizer.scale=1; 
    } 
    else 
    { 
     currentLabel.font = [UIFont fontWithName:currentLabel.font.fontName size:(currentLabel.font.pointSize + pinchScale)]; 

     recognizer.view.transform = CGAffineTransformScale(recognizer.view.transform, recognizer.scale, recognizer.scale); 

     [currentLabel sizeToFit]; 

     recognizer.scale=1; 
    } 
    //currentLabel.adjustsFontSizeToFitWidth = YES; 

    // [currentLabel sizeToFit]; 
    NSLog(@"Font :%@",label.font); 
} 
+0

你应该更新你的格式。阅读并不容易。 – 2014-05-28 15:11:46

+0

ohhhh多数民众赞成在非常好它的真正工作thx ...! 但我想改变我的标签框架在这种方法我尝试它,但它不是完美的水平和垂直捏完成它改变它的高度和宽度,所以你可以帮我... THX ...先生Arvind库马尔。 。:) – 2015-04-16 12:22:07

- (void)pinchToZoom:(UIPinchGestureRecognizer*)gesture 
{ 
    switch (gesture.state) 
    { 
     case UIGestureRecognizerStateBegan: 
     { 
      lastScale = gesture.scale; 
     }break; 
     case UIGestureRecognizerStateChanged: 
     { 
      const CGFloat zoomSensitivity = 5; 
      const CGFloat zoomMin = 1; 
      const CGFloat zoomMax = 16; 

      CGFloat objectScale = gesture.view.contentScaleFactor; 
      CGFloat zoomDiff = lastScale - gesture.scale; 
      CGFloat zoomDirty = objectScale - zoomDiff * zoomSensivity; 
      CGFloat zoomTo = fmaxf(zoomMin, fminf(zoomDirty, zoomMax)); 

      // step round if needed (neutralize elusive changes) 
      zoomTo = (NSInteger)(zoomTo * 10) * 0.1; 

      if (objectScale != zoomTo) 
       gesture.view.contentScaleFactor = zoomTo; 

      lastScale = gesture.scale; 
     }break; 
     default: 
      break; 
    } 
}