使用UIPinchGestureRecognizer以单向方式缩放uiviews

我想知道我们如何使用UIPinchGestureRecognizer来单独在单个(x或y)方向上扩展UIView 。 例如,如果用户只在一个方向上(水平)移动他的两个手指,则只有该视图的宽度应该增大/减小,并且如果手指仅仅垂直移动,则高度应该改变。 如果手指沿对angular线移动,则uview的高度和宽度应增加/减小。 我已经看到了来自苹果的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 

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

  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; } 

另一种做法是:

  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来计算两次触摸之间的距离,并select要使用哪个视图来计算距离( yourViewHere )。

编辑:

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

编辑2:关于计算缩放的公式

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

  1. 比例因子增量是: 1-gestureRecognizer.scale ;

  2. 三angular洲乘以一个因素,以便它在某种程度上正比于沿着水平轴或垂直轴的位移; 当位移为零时,三angular形尺度也为零; 当两个轴线的位移相等时,标尺的三angular形沿两个轴线也是相等的; 我决定除以distance ,但还有其他的可能性(如除以两个deltaMoves的总和或deltaMoves的最大值等)。

  3. 最后从元素的当前比例中减去调整的增量。

今天我面临同样的问题,我发现一个简单而简单的方法来做到这一点

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

我创build了一个UIPinchGestureRecognizer的自定义版本来完成你正在寻找的东西。 它使用两根手指之间的线的斜率来确定标尺的方向。 它有3种types:垂直; 水平; 和组合(对angular线)。 请在底部看我的笔记。

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

这是设置为使用主视图捕捉捏; 并操纵第二个视图。 这样,您仍然可以缩放视图变小。 您可以将其更改为直接对可缩放视图作出反应。

限制:我任意select我的视图的开始大小,所以2.0的限制等于全屏。 我的下限是0.1。

用户交互:我篡改了很多用户交互的东西,比如改变视图的背景颜色和在视图上添加/改变箭头以显示方向。 在缩放过程中给予他们反馈是非常重要的,特别是当这些代码允许的情况下改变方向时。

BUG:苹果的UIPinchGestureRecognizer有一个错误。 它会按照您所期望的那样通过两根手指的触摸来注册UIGestureRecognizerStateBegan。 但是,一旦处于StateBegan或StateChanged状态,您可以提起一个手指,并保持状态。 它不会移动到StateEnded或StateCancelled直到两个手指被解除。 这在我的代码中造成了一个错误,并且让我头疼! 如果numberOfTouches> 1修复它。

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