使用UIPinchGestureRecognizer在单向上缩放uiviews

时间:2011-07-20 08:25:43

标签: iphone objective-c ios ipad uigesturerecognizer

我想知道我们如何使用UIPinchGestureRecognizer仅在单个(x或y)方向上缩放UIView。比如,如果用户仅在单个方向(水平)上以捏合手势移动他的两个手指,则仅uiview的宽度应该增加/减小,并且如果手指仅垂直移动,则高度应该改变。如果手指沿对角线移动,则uiview的高度和宽度都应增加/减少。我见过Apple的MoveMe示例代码。

UIPinchGestureRecognizer *pinchGesture = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(scalePiece:)];
[pinchGesture setDelegate:self];
[piece addGestureRecognizer:pinchGesture];
[pinchGesture release];

比例片:

- (void)scalePiece:(UIPinchGestureRecognizer *)gestureRecognizer
{
    UIView *piece = (UIView *) [gestureRecognizer view];
    NSLog(@"scalePiece enter");
    if ([gestureRecognizer state] == UIGestureRecognizerStateBegan){
          NSLog(@"inside if");
          lastTouchPosition = [gestureRecognizer locationInView:piece];
    } 
    else if ([gestureRecognizer state] == UIGestureRecognizerStateBegan || [gestureRecognizer state] == UIGestureRecognizerStateChanged){
          NSLog(@"inside else");
          CGPoint currentTouchLocation = [gestureRecognizer locationInView:piece];
          NSLog(@"currentTouchLocation = %@ and lastTouchPosition= %@",NSStringFromCGPoint(currentTouchLocation), NSStringFromCGPoint(lastTouchPosition));
          CGPoint deltaMove = [self calculatePointDistancewithPoint1:currentTouchLocation andPoint2:lastTouchPosition];
          NSLog(@"deltaMove = %@",NSStringFromCGPoint(deltaMove));
          float distance = sqrt(deltaMove.x*deltaMove.x + deltaMove.y*deltaMove.y);
          NSLog(@"distance = %f",distance);
          float hScale = 1 - deltaMove.x/distance * (1-gestureRecognizer.scale);
          float vScale = 1 - deltaMove.y/distance * (1-gestureRecognizer.scale);
          if (distance == 0) {
                     hScale = 1;
                     vScale = 1;
          }
          NSLog(@"[gestureRecognizer scale] = %f",[gestureRecognizer scale]);
          NSLog(@"hScale = %f and vScale = %f",hScale, vScale);
          piece.transform = CGAffineTransformScale([piece transform], hScale, vScale);
          [gestureRecognizer setScale:1];
          lastTouchPosition = currentTouchLocation;
    }
    NSLog(@"scalePiece exit");
}

计算距离:

- (CGPoint) calculatePointDistancewithPoint1:(CGPoint)point1 andPoint2:(CGPoint) point2 {
    return CGPointMake(point2.x - point1.x, point2.y - point1.y);
}

这是当我试图捏出(放大)视图时仅在垂直方向上移动手指时的日志输出。元素的高度不会增加。

2011-07-21 13:06:56.245 New[8169:707] scalePiece enter
2011-07-21 13:06:56.248 New[8169:707] inside if
2011-07-21 13:06:56.251 New[8169:707] scalePiece exit
2011-07-21 13:06:56.259 New[8169:707] scalePiece enter
2011-07-21 13:06:56.262 New[8169:707] inside else
2011-07-21 13:06:56.264 New[8169:707] currentTouchLocation = {88, 87} and lastTouchPosition= {87, 86}
2011-07-21 13:06:56.265 New[8169:707] deltaMove = {-1, -1}
2011-07-21 13:06:56.267 New[8169:707] distance = 1.414214
2011-07-21 13:06:56.268 New[8169:707] [gestureRecognizer scale] = 1.102590
2011-07-21 13:06:56.271 New[8169:707] hScale = 0.927458 and vScale = 0.927458
2011-07-21 13:06:56.272 New[8169:707] scalePiece exit
2011-07-21 13:06:56.281 New[8169:707] scalePiece enter
2011-07-21 13:06:56.283 New[8169:707] inside else
2011-07-21 13:06:56.284 New[8169:707] currentTouchLocation = {87, 89} and lastTouchPosition= {88, 87}
2011-07-21 13:06:56.286 New[8169:707] deltaMove = {1, -2}
2011-07-21 13:06:56.287 New[8169:707] distance = 2.236068
2011-07-21 13:06:56.296 New[8169:707] [gestureRecognizer scale] = 1.096172
2011-07-21 13:06:56.298 New[8169:707] hScale = 1.043009 and vScale = 0.913981
2011-07-21 13:06:56.299 New[8169:707] scalePiece exit
2011-07-21 13:06:56.302 New[8169:707] scalePiece enter
2011-07-21 13:06:56.303 New[8169:707] inside else
2011-07-21 13:06:56.305 New[8169:707] currentTouchLocation = {88, 89} and lastTouchPosition= {87, 89}
2011-07-21 13:06:56.309 New[8169:707] deltaMove = {-1, 0}
2011-07-21 13:06:56.311 New[8169:707] distance = 1.000000
2011-07-21 13:06:56.313 New[8169:707] [gestureRecognizer scale] = 1.066320
2011-07-21 13:06:56.314 New[8169:707] hScale = 0.933680 and vScale = 1.000000
2011-07-21 13:06:56.316 New[8169:707] scalePiece exit
2011-07-21 13:06:56.318 New[8169:707] scalePiece enter
2011-07-21 13:06:56.320 New[8169:707] inside else
2011-07-21 13:06:56.329 New[8169:707] currentTouchLocation = {88, 90} and lastTouchPosition= {88, 89}
2011-07-21 13:06:56.331 New[8169:707] deltaMove = {0, -1}
2011-07-21 13:06:56.333 New[8169:707] distance = 1.000000
2011-07-21 13:06:56.334 New[8169:707] [gestureRecognizer scale] = 1.061696
2011-07-21 13:06:56.335 New[8169:707] hScale = 1.000000 and vScale = 0.938304
2011-07-21 13:06:56.338 New[8169:707] scalePiece exit
2011-07-21 13:06:56.343 New[8169:707] scalePiece enter
2011-07-21 13:06:56.346 New[8169:707] inside else
2011-07-21 13:06:56.347 New[8169:707] currentTouchLocation = {88, 92} and lastTouchPosition= {88, 90}
2011-07-21 13:06:56.349 New[8169:707] deltaMove = {0, -2}
2011-07-21 13:06:56.350 New[8169:707] distance = 2.000000
2011-07-21 13:06:56.351 New[8169:707] [gestureRecognizer scale] = 1.096869
2011-07-21 13:06:56.353 New[8169:707] hScale = 1.000000 and vScale = 0.903131
2011-07-21 13:06:56.362 New[8169:707] scalePiece exit
2011-07-21 13:06:56.366 New[8169:707] scalePiece enter
2011-07-21 13:06:56.370 New[8169:707] inside else
2011-07-21 13:06:56.373 New[8169:707] currentTouchLocation = {88, 92} and lastTouchPosition= {88, 92}
2011-07-21 13:06:56.376 New[8169:707] deltaMove = {0, 0}
2011-07-21 13:06:56.380 New[8169:707] distance = 0.000000
2011-07-21 13:06:56.383 New[8169:707] [gestureRecognizer scale] = 1.035330
2011-07-21 13:06:56.387 New[8169:707] hScale = 1.000000 and vScale = 1.000000
2011-07-21 13:06:56.389 New[8169:707] scalePiece exit
2011-07-21 13:06:56.393 New[8169:707] scalePiece enter
2011-07-21 13:06:56.397 New[8169:707] inside else
2011-07-21 13:06:56.399 New[8169:707] currentTouchLocation = {88, 93} and lastTouchPosition= {88, 92}
2011-07-21 13:06:56.403 New[8169:707] deltaMove = {0, -1}
2011-07-21 13:06:56.406 New[8169:707] distance = 1.000000
2011-07-21 13:06:56.409 New[8169:707] [gestureRecognizer scale] = 1.042659
2011-07-21 13:06:56.412 New[8169:707] hScale = 1.000000 and vScale = 0.957341
2011-07-21 13:06:56.414 New[8169:707] scalePiece exit
2011-07-21 13:06:56.419 New[8169:707] scalePiece enter
2011-07-21 13:06:56.422 New[8169:707] inside else
2011-07-21 13:06:56.425 New[8169:707] currentTouchLocation = {88, 92} and lastTouchPosition= {88, 93}
2011-07-21 13:06:56.427 New[8169:707] deltaMove = {0, 1}
2011-07-21 13:06:56.430 New[8169:707] distance = 1.000000
2011-07-21 13:06:56.432 New[8169:707] [gestureRecognizer scale] = 1.024549
2011-07-21 13:06:56.436 New[8169:707] hScale = 1.000000 and vScale = 1.024549
2011-07-21 13:06:56.439 New[8169:707] scalePiece exit
2011-07-21 13:06:56.442 New[8169:707] scalePiece enter
2011-07-21 13:06:56.447 New[8169:707] inside else
2011-07-21 13:06:56.450 New[8169:707] currentTouchLocation = {88, 92} and lastTouchPosition= {88, 92}
2011-07-21 13:06:56.453 New[8169:707] deltaMove = {0, 0}
2011-07-21 13:06:56.455 New[8169:707] distance = 0.000000
2011-07-21 13:06:56.458 New[8169:707] [gestureRecognizer scale] = 1.007702
2011-07-21 13:06:56.460 New[8169:707] hScale = 1.000000 and vScale = 1.000000
2011-07-21 13:06:56.464 New[8169:707] scalePiece exit
2011-07-21 13:06:56.501 New[8169:707] scalePiece enter
2011-07-21 13:06:56.504 New[8169:707] inside else
2011-07-21 13:06:56.507 New[8169:707] currentTouchLocation = {89, 92} and lastTouchPosition= {88, 92}
2011-07-21 13:06:56.509 New[8169:707] deltaMove = {-1, 0}
2011-07-21 13:06:56.510 New[8169:707] distance = 1.000000
2011-07-21 13:06:56.511 New[8169:707] [gestureRecognizer scale] = 1.000283
2011-07-21 13:06:56.513 New[8169:707] hScale = 0.999717 and vScale = 1.000000
2011-07-21 13:06:56.517 New[8169:707] scalePiece exit
2011-07-21 13:06:56.566 New[8169:707] scalePiece enter
2011-07-21 13:06:56.570 New[8169:707] inside else
2011-07-21 13:06:56.572 New[8169:707] currentTouchLocation = {89, 91} and lastTouchPosition= {89, 92}
2011-07-21 13:06:56.573 New[8169:707] deltaMove = {0, 1}
2011-07-21 13:06:56.575 New[8169:707] distance = 1.000000
2011-07-21 13:06:56.576 New[8169:707] [gestureRecognizer scale] = 1.008267
2011-07-21 13:06:56.579 New[8169:707] hScale = 1.000000 and vScale = 1.008267
2011-07-21 13:06:56.582 New[8169:707] scalePiece exit
2011-07-21 13:06:56.585 New[8169:707] scalePiece enter
2011-07-21 13:06:56.586 New[8169:707] inside else
2011-07-21 13:06:56.588 New[8169:707] currentTouchLocation = {89, 91} and lastTouchPosition= {89, 91}
2011-07-21 13:06:56.589 New[8169:707] deltaMove = {0, 0}
2011-07-21 13:06:56.591 New[8169:707] distance = 0.000000
2011-07-21 13:06:56.597 New[8169:707] [gestureRecognizer scale] = 1.000000
2011-07-21 13:06:56.599 New[8169:707] hScale = 1.000000 and vScale = 1.000000
2011-07-21 13:06:56.600 New[8169:707] scalePiece exit
2011-07-21 13:06:56.603 New[8169:707] scalePiece enter
2011-07-21 13:06:56.604 New[8169:707] inside else
2011-07-21 13:06:56.606 New[8169:707] currentTouchLocation = {89, 182} and lastTouchPosition= {89, 91}
2011-07-21 13:06:56.607 New[8169:707] deltaMove = {0, -91}
2011-07-21 13:06:56.617 New[8169:707] distance = 91.000000
2011-07-21 13:06:56.620 New[8169:707] [gestureRecognizer scale] = 1.000000
2011-07-21 13:06:56.623 New[8169:707] hScale = 1.000000 and vScale = 1.000000
2011-07-21 13:06:56.626 New[8169:707] scalePiece exit
2011-07-21 13:06:56.630 New[8169:707] scalePiece enter
2011-07-21 13:06:56.632 New[8169:707] scalePiece exit

3 个答案:

答案 0 :(得分:8)

如果我正确理解了您的问题,您的目标是沿水平轴和垂直轴进行非比例缩放。在这种情况下,事情归结为提供你的仿射变换:

  piece.transform = CGAffineTransformScale([piece transform], hScale, vScale);

具有不同的缩放因子。

计算它们的一种方法如下:

  1. 在您的班级中定义一个ivar来存储lastTouchPosition;

  2. 在您的手势处理程序中,您将执行以下操作:

    if ([gestureRecognizer state] == UIGestureRecognizerStateBegan){
    
       lastTouchPosition = [gestureRecognize locationInView:yourViewHere];
       hScale = 1;
       vScale = 1;
    
    } else if ([gestureRecognizer state] == UIGestureRecognizerStateBegan || [gestureRecognizer state] == UIGestureRecognizerStateChanged){
    
       CGPoint currentTouchLocation = [gestureRecognize locationInView:yourViewHere];
       CGPoint deltaMove = CGPointDistance(currentTouchLocation, lastTouchPosition);
       float distance = sqrt(deltaMove.x*deltaMove.x + deltaMove.y*deltaMove.y);
       hScale -= abs(deltaMove.x)/distance * (1-gestureRecognizer.scale);
       vScale -= abs(deltaMove.y)/distance * (1-gestureRecognizer.scale);
       piece.transform = CGAffineTransformScale([piece transform], hScale, vScale);
       [gestureRecognizer setScale:1];
    
       lastTouchPosition = currentTouchLocation;
    }
    
  3. 另一种做法是:

        if ([gestureRecognizer state] == UIGestureRecognizerStateBegan){
    
           lastTouchPosition = [gestureRecognize locationInView:yourViewHere];
    
        } else if ([gestureRecognizer state] == UIGestureRecognizerStateBegan || [gestureRecognizer state] == UIGestureRecognizerStateChanged){
    
           CGPoint currentTouchLocation = [gestureRecognize locationInView:yourViewHere];
           CGPoint deltaMove = CGPointDistance(currentTouchLocation, lastTouchPosition);
           float distance = sqrt(deltaMove.x*deltaMove.x + deltaMove.y*deltaMove.y);
           float hScale = 1 - abs(deltaMove.x)/distance * (1-gestureRecognizer.scale);
           float vScale = 1 - abs(deltaMove.y)/distance * (1-gestureRecognizer.scale);
           piece.transform = CGAffineTransformScale([piece transform], hScale, vScale);
    
           lastTouchPosition = currentTouchLocation;
        }
    

    这不会在每次迭代时存储当前hFloatvFloat,而是依赖于gestureRecognizer将累积整体规模变化的事实。它执行“绝对”计算,而第一个实现执行“相对”计算。

    请注意,您还需要定义CGPointDistance来计算两次触摸之间的距离,并选择要用于计算距离的视图(yourViewHere)。

    编辑:

    CGPoint CGPointDistance(CGPoint point1, CGPoint point2)
    {
        return = CGPointMake(point2.x - point1.x, point2.y - point1.y);
    };
    

    EDIT2:关于计算缩放的公式

    这个想法是计算比例因子的变化,并根据它们的相对变化将它应用于两个方向(x和y)。

    1. 比例因子delta为:1-gestureRecognizer.scale;

    2. 将delta乘以一个因子,使其在某种程度上与沿水平或垂直轴的位移成比例;当位移为零时,三角尺度也为零;当位移沿两个轴线相等时,刻度差值沿两个轴线也相等;我决定除以distance,但还有其他可能性(比如除以两个deltaMoves的总和或deltaMoves的最大值等)。

    3. 最终从元素的当前比例中减去调整后的差值。

答案 1 :(得分:5)

今天我遇到了同样的问题,我找到了一个简单而简短的方法来做到这一点

- (IBAction)handlePinch:(UIPinchGestureRecognizer *)recognizer
{
  recognizer.view.transform = CGAffineTransformScale(recognizer.view.transform, recognizer.scale,1);
  recognizer.scale = 1;
}

答案 2 :(得分:2)

我创建了一个UIPinchGestureRecognizer的自定义版本,可以完全满足您的需求。它使用两个手指之间的直线斜率来确定刻度的方向。它有3种类型:垂直;水平;和组合(对角线)。请在底部查看我的笔记。

-(void) scaleTheView:(UIPinchGestureRecognizer *)pinchRecognizer
    {
    if ([pinchRecognizer state] == UIGestureRecognizerStateBegan || [pinchRecognizer state] == UIGestureRecognizerStateChanged) {

    if ([pinchRecognizer numberOfTouches] > 1) {

        UIView *theView = [pinchRecognizer view];

        CGPoint locationOne = [pinchRecognizer locationOfTouch:0 inView:theView];
        CGPoint locationTwo = [pinchRecognizer locationOfTouch:1 inView:theView];
            NSLog(@"touch ONE  = %f, %f", locationOne.x, locationOne.y);
            NSLog(@"touch TWO  = %f, %f", locationTwo.x, locationTwo.y);
        [scalableView setBackgroundColor:[UIColor redColor]];

        if (locationOne.x == locationTwo.x) {
                // perfect vertical line
                // not likely, but to avoid dividing by 0 in the slope equation
            theSlope = 1000.0;
        }else if (locationOne.y == locationTwo.y) {
                // perfect horz line
                // not likely, but to avoid any problems in the slope equation
            theSlope = 0.0;
        }else {
            theSlope = (locationTwo.y - locationOne.y)/(locationTwo.x - locationOne.x);
        }

        double abSlope = ABS(theSlope);

        if (abSlope < 0.5) {
                    //  Horizontal pinch - scale in the X
            [arrows setImage:[UIImage imageNamed:@"HorzArrows.png"]];
            arrows.hidden = FALSE;
                    // tranform.a  = X-axis
                NSLog(@"transform.A = %f", scalableView.transform.a);
                    // tranform.d  = Y-axis
                NSLog(@"transform.D = %f", scalableView.transform.d);

                    //  if hit scale limit along X-axis then stop scale and show Blocked image
            if (((pinchRecognizer.scale > 1.0) && (scalableView.transform.a >= 2.0)) || ((pinchRecognizer.scale < 1.0) && (scalableView.transform.a <= 0.1))) {
                blocked.hidden = FALSE;
                arrows.hidden = TRUE;
            } else {
                        // scale along X-axis
                scalableView.transform = CGAffineTransformScale(scalableView.transform, pinchRecognizer.scale, 1.0);
                pinchRecognizer.scale = 1.0;
                blocked.hidden = TRUE;
                arrows.hidden = FALSE;
            }
        }else if (abSlope > 1.7) {
                    // Vertical pinch - scale in the Y
            [arrows setImage:[UIImage imageNamed:@"VerticalArrows.png"]];
            arrows.hidden = FALSE;
                NSLog(@"transform.A = %f", scalableView.transform.a);
                NSLog(@"transform.D = %f", scalableView.transform.d);

                    //  if hit scale limit along Y-axis then don't scale and show Blocked image
            if (((pinchRecognizer.scale > 1.0) && (scalableView.transform.d >= 2.0)) || ((pinchRecognizer.scale < 1.0) && (scalableView.transform.d <= 0.1))) {
                blocked.hidden = FALSE;
                arrows.hidden = TRUE;
            } else {
                        // scale along Y-axis
                scalableView.transform = CGAffineTransformScale(scalableView.transform, 1.0, pinchRecognizer.scale);
                pinchRecognizer.scale = 1.0;
                blocked.hidden = TRUE;
                arrows.hidden = FALSE;
            }
        } else {
                    // Diagonal pinch - scale in both directions
            [arrows setImage:[UIImage imageNamed:@"CrossArrows.png"]];
            blocked.hidden = TRUE;
            arrows.hidden = FALSE;

                NSLog(@"transform.A = %f", scalableView.transform.a);
                NSLog(@"transform.D = %f", scalableView.transform.d);

                    // if we have hit any limit don't allow scaling
            if ((((pinchRecognizer.scale > 1.0) && (scalableView.transform.a >= 2.0)) || ((pinchRecognizer.scale < 1.0) && (scalableView.transform.a <= 0.1))) || (((pinchRecognizer.scale > 1.0) && (scalableView.transform.d >= 2.0)) || ((pinchRecognizer.scale < 1.0) && (scalableView.transform.d <= 0.1)))) {
                blocked.hidden = FALSE;
                arrows.hidden = TRUE;
            } else {
                        // scale in both directions
                scalableView.transform = CGAffineTransformScale(scalableView.transform, pinchRecognizer.scale, pinchRecognizer.scale);
                pinchRecognizer.scale = 1.0;
                blocked.hidden = TRUE;
                arrows.hidden = FALSE;
            }
        }  // else for diagonal pinch
    }  // if numberOfTouches
}  // StateBegan if

if ([pinchRecognizer state] == UIGestureRecognizerStateEnded || [pinchRecognizer state] == UIGestureRecognizerStateCancelled) {
    NSLog(@"StateEnded StateCancelled");
    [scalableView setBackgroundColor:[UIColor whiteColor]];
    arrows.hidden = TRUE;
    blocked.hidden = TRUE;
    }
}

请记住将协议添加到视图控制器头文件中:

@interface WhiteViewController : UIViewController <UIGestureRecognizerDelegate>
{
IBOutlet UIView *scalableView;
IBOutlet UIView *mainView;
IBOutlet UIImageView *arrows;
IBOutlet UIImageView *blocked;
}
@property (strong, nonatomic) IBOutlet UIView *scalableView;
@property (strong, nonatomic) IBOutlet UIView *mainView;
@property (strong, nonatomic)IBOutlet UIImageView *arrows;
@property (strong, nonatomic)IBOutlet UIImageView *blocked;

-(void) scaleTheView:(UIPinchGestureRecognizer *)pinchRecognizer;
@end

在viewDidLoad中添加识别器:

- (void)viewDidLoad
{ UIPinchGestureRecognizer *pinchGesture = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(scaleTheView:)];
[pinchGesture setDelegate:self];
[mainView addGestureRecognizer:pinchGesture];

arrows.hidden = TRUE;
blocked.hidden = TRUE;
[scalableView setBackgroundColor:[UIColor whiteColor]];}

设置为使用主视图捕捉捏;并操纵第二个视图。这样,您可以在视图变小时进行缩放。您可以更改它以直接对可伸缩视图做出反应。

限制:我随意选择了我的视图的起始大小,因此缩放限制为2.0将等于全屏。我的较低比例设定为0.1。

用户交互:我搞砸了许多用户交互的事情,比如更改视图的背景颜色以及在视图上添加/更改箭头以显示方向。在缩放过程中给予他们反馈非常重要,尤其是在更改此代码允许的方向时。

BUG:Apple的UIPinchGestureRecognizer存在一个错误。它可以像你期望的那样用两根手指触摸UIGestureRecognizerStateBegan。但是一旦它在StateBegan或StateChanged,你可以抬起一根手指,状态仍然存在。在手指抬起之前,它不会移动到StateEnded或StateCancelled。这在我的代码中造成了一个错误,并且令人头疼! if numberOfTouches&gt; 1修复它。

未来:您可以将坡度设置更改为仅在一个方向上缩放,或者只是2.如果添加箭头图像,则可以在旋转手指时看到它们发生变化。