Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
264 views
in Technique[技术] by (71.8m points)

iphone - Using UIPinchGestureRecognizer to scale uiviews in single direction

I would like to know how we can use UIPinchGestureRecognizer to scale UIView in single (x or y) directions alone. Say, if the user moves his two fingers in a pinch gesture only in a single direction (horizontal), only the width of the uiview should increase/decrease and if the fingers are moved only vertically, the height should change. If the fingers move diagonally, then both height and width of uiview should increase/decrease. I have seen the MoveMe sample code from Apple.

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

Scale piece:

- (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");
}

Calculate distance:

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

This is the log output when I try to pinch out (zoom into) of the view only moving my fingers in a vertical direction. The element's height does not increase.

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
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

If I understand correctly your question, you are aiming at having non-proportional scaling along the horizontal and vertical axis. In this case, the thing comes down to providing your affine transform:

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

with different scaling factors.

One way to calculate them is the following:

  1. define an ivar in your class to store the lastTouchPosition;

  2. in your gesture handler you will do something like this:

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

An alternative way of doing is:

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

This will not store at each iteration the current hFloat and vFloat and instead rely on the fact that the gestureRecognizer will accumulate the overall scale change. It does an "absolute" calculate, while the first implementation does a "relative" one.

Note that you need also define CGPointDistance to calculate the distance between the two touches and choose which view are your going to use to calculate the distance (yourViewHere).

EDIT:

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

EDIT2: about the formula to calculate the scaling

The idea is calculating the variation in the scale factor and apply it to the two directions (x and y) according to their relative variations.

  1. the scale factor delta is: 1-gestureRecognizer.scale;

  2. the delta is the multiplied by a factor so that it is somehow proportional to the displacement along the horizontal or vertical axis; when the displacement is zero, the delta scale is also zero; when the displacement is equal along the two axis, the scale delta is also equal along the two axis; I decided to divide by the distance, but there are other possibilities (like dividing by the sum of the two deltaMoves or by the max of the deltaMoves, etc).

  3. the adjusted delta is finally subtracted from the current scale of the element.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...