我想知道我们如何使用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
答案 0 :(得分:8)
如果我正确理解了您的问题,您的目标是沿水平轴和垂直轴进行非比例缩放。在这种情况下,事情归结为提供你的仿射变换:
piece.transform = CGAffineTransformScale([piece transform], hScale, vScale);
具有不同的缩放因子。
计算它们的一种方法如下:
在您的班级中定义一个ivar来存储lastTouchPosition
;
在您的手势处理程序中,您将执行以下操作:
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;
}
这不会在每次迭代时存储当前hFloat
和vFloat
,而是依赖于gestureRecognizer
将累积整体规模变化的事实。它执行“绝对”计算,而第一个实现执行“相对”计算。
请注意,您还需要定义CGPointDistance
来计算两次触摸之间的距离,并选择要用于计算距离的视图(yourViewHere
)。
编辑:
CGPoint CGPointDistance(CGPoint point1, CGPoint point2)
{
return = CGPointMake(point2.x - point1.x, point2.y - point1.y);
};
EDIT2:关于计算缩放的公式
这个想法是计算比例因子的变化,并根据它们的相对变化将它应用于两个方向(x和y)。
比例因子delta为:1-gestureRecognizer.scale
;
将delta乘以一个因子,使其在某种程度上与沿水平或垂直轴的位移成比例;当位移为零时,三角尺度也为零;当位移沿两个轴线相等时,刻度差值沿两个轴线也相等;我决定除以distance
,但还有其他可能性(比如除以两个deltaMoves的总和或deltaMoves的最大值等)。
最终从元素的当前比例中减去调整后的差值。
答案 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.如果添加箭头图像,则可以在旋转手指时看到它们发生变化。