Best way to do multi-touch scale/rotate on iPhone

Discussion in 'iOS Programming' started by luckylefty01, Jun 29, 2008.

  1. macrumors member

    Joined:
    Apr 8, 2008
    #1
    This is the current code I'm using to do your typical iPhone rotate/scale with two fingers on a view. I did it using your fairly typical pythagorean theorem and trig combination.

    What I'm wondering is if there's a better/cleaner/more efficient mathematically way to do it?

    This is an example of how it could be used in program:
    Code:
    view.transform = CGAffineTransformWithTouches(view.transform, firstTouch, secondTouch);
    [UIView commitAnimations];
    The code:
    Code:
    CGFloat distanceBetweenPoints(CGPoint firstPoint, CGPoint secondPoint) {
      CGFloat distance;
      
      //Square difference in x
      CGFloat xDifferenceSquared = pow(firstPoint.x - secondPoint.x, 2);
      //  NSLog(@"xDifferenceSquared: %f", xDifferenceSquared);
      
      // Square difference in y
      CGFloat yDifferenceSquared = pow(firstPoint.y - secondPoint.y, 2);
      //  NSLog(@"yDifferenceSquared: %f", yDifferenceSquared);
      
      // Add and take Square root
      distance = sqrt(xDifferenceSquared + yDifferenceSquared);
      //  NSLog(@"Distance: %f", distance);
      return distance;
      
    }
    
    CGPoint vectorBetweenPoints(CGPoint firstPoint, CGPoint secondPoint) {
    //  NSLog(@"Point One: %f, %f", firstPoint.x, firstPoint.y);
    //  NSLog(@"Point Two: %f, %f", secondPoint.x, secondPoint.y);
      
      CGFloat xDifference = firstPoint.x - secondPoint.x;
      CGFloat yDifference = firstPoint.y - secondPoint.y;
      
      CGPoint result = CGPointMake(xDifference, yDifference);
      
      return result;
    }
    
    CGAffineTransform CGAffineTransformWithTouches(CGAffineTransform oldTransform, 
                                                   UITouch *firstTouch, 
                                                   UITouch *secondTouch) {
      
      CGPoint firstTouchLocation = [firstTouch locationInView:nil];
      CGPoint firstTouchPreviousLocaion = [firstTouch previousLocationInView:nil];
      CGPoint secondTouchLocation = [secondTouch locationInView:nil];
      CGPoint secondTouchPreviousLocaion = [secondTouch previousLocationInView:nil];
    
      CGAffineTransform newTransform;
      
      // Calculate new scale and rotation
      
      // Get distance between points
      CGFloat currentDistance = distanceBetweenPoints(firstTouchLocation,
                                                      secondTouchLocation);
      
      CGFloat previousDistance = distanceBetweenPoints(firstTouchPreviousLocaion,
                                                       secondTouchPreviousLocaion);
      
      // Figure new scale
      
      CGFloat distanceRatio = currentDistance / previousDistance;
    //  NSLog(@"DifferenceRatio: %f, %f", distanceRatio.x, distanceRatio.y);
     
      // Combine with current transform
      newTransform = CGAffineTransformScale(oldTransform, distanceRatio, distanceRatio);
     
      
      // Now figure new rotation
    
      // Get previous angle
      CGPoint previousDifference = vectorBetweenPoints(firstTouchPreviousLocaion, secondTouchPreviousLocaion);
      CGFloat xDifferencePrevious = previousDifference.x;
    
      // Use acos to get angle to avoid issue with the denominator being 0
      CGFloat previousRotation = acos(xDifferencePrevious / previousDistance); // adjacent over hypotenuse
      if (previousDifference.y < 0) {
        // account for acos of angles in quadrants III and IV
        previousRotation *= -1;
      }
    //  NSLog(@"previousRotation: %f", previousRotation);
      
      // Get current angle
      CGPoint currentDifference = vectorBetweenPoints(firstTouchLocation, secondTouchLocation);
      CGFloat xDifferenceCurrent = currentDifference.x;
      
      // Use acos to get angle to avoid issue with the denominator being 0
      CGFloat currentRotation = acos(xDifferenceCurrent / currentDistance); // adjacent over hypotenuse 
      if (currentDifference.y < 0) {
        // account for acos of angles in quadrants III and IV
        currentRotation *= -1;
      }
    //  NSLog(@"currentRotation: %f", currentRotation);
      
      CGFloat newAngle = currentRotation - previousRotation;
      
      // Combine with current transform
      newTransform = CGAffineTransformRotate(newTransform, newAngle);
      
      // Return result
      return newTransform;
    }
     
  2. macrumors 6502

    Joined:
    May 9, 2008
    Location:
    Bangalore
    #2
    Hi...

    For a beginner like me, it looks pretty "BIG STUFF"...
    In my iPhone app, i did return YES for all orientation...

    if i take that view in portrait mode it will fix perfectly to that view...
    But if i rotate to landscape mode, view is rotating, but bounds are same as previous.. means in lanscape mode, i can see the view rotated, but occupying half the width, and more hieght...

    Help....
     
  3. macrumors 6502

    Joined:
    Jul 25, 2006
    #3
    I don't know if there's any existing functionality for doing this, but I think you've got the right idea.

    For code brevity's sake, you can use hypot() to calculate the hypotenuse of a triangle and you can use atan2(), which will put the angle in the proper quadrant for you. (These are common Unix functions, but I don't know for sure if they're on the phone.)
     
  4. thread starter macrumors member

    Joined:
    Apr 8, 2008
    #4
    Thanks Kupa, I hadn't seen those before. They're both in math.h so they should definitely be on the phone.
     
  5. macrumors regular

    Joined:
    Jul 28, 2008
    #5
    Hi,

    I am trying to modify your code and try rotate and stretch in one direction. How can i accomplish this ? my code is as follows

    CGAffineTransform CGAffineTransformWithTouches(CGAffineTransform oldTransform,
    /*UITouch *firstTouch,*/
    UITouch *secondTouch) {

    CGPoint secondTouchLocation = [secondTouch locationInView:nil];
    CGPoint secondTouchPreviousLocaion = [secondTouch previousLocationInView:nil];

    CGAffineTransform newTransform;

    // Calculate new scale and rotation

    // Get distance between points
    CGFloat currentDistance = distanceBetweenPoints(secondTouchLocation, secondTouchPreviousLocaion);

    // Figure new scale

    CGFloat distanceRatio = currentDistance ;/// previousDistance;

    // Combine with current transform
    newTransform = CGAffineTransformScale(oldTransform, distanceRatio, distanceRatio);

    CGPoint currentDifference = vectorBetweenPoints(secondTouchLocation, secondTouchPreviousLocaion);
    CGFloat xDifferenceCurrent = currentDifference.x;

    // Use acos to get angle to avoid issue with the denominator being 0
    CGFloat currentRotation = acos(xDifferenceCurrent / currentDistance); // adjacent over hypotenuse
    if (currentDifference.y < 0) {
    // account for acos of angles in quadrants III and IV
    currentRotation *= -1;
    }

    //CGFloat newAngle = currentRotation - previousRotation;

    // Combine with current transform
    newTransform = CGAffineTransformRotate(newTransform, currentRotation);
    // newTransform = CGAffineTransformMakeRotation(currentRotation);

    // Return result
    return newTransform;
    }

    the thing is, it is stretching but when it rotates, it starts stretching vertically, which i dont want. i want only stretched towards the direction where touch moves.

    basically what does a, b, c, d and tx ty in cgaffine struct mean ?

    please gimme some ideas

    Jagat
     
  6. macrumors newbie

    narut

    Joined:
    Nov 2, 2008
    #6
    I know this thread is a bit old, but just in case it might be useful for someone looking for the easy way to rotate view (rotate only, no zoom) with two fingers.

    Here's my code.
    Code:
    UITouch *touch1 = [[allTouches allObjects] objectAtIndex:0];
    UITouch *touch2 = [[allTouches allObjects] objectAtIndex:1];
    CGPoint previousPoint1 = [touch1 previousLocationInView:nil];
    CGPoint previousPoint2 = [touch2 previousLocationInView:nil];
    CGFloat previousAngle = atan2 (previousPoint2.y - previousPoint1.y, previousPoint2.x - previousPoint1.x);
    				
    CGPoint currentPoint1 = [touch1 locationInView:nil];
    CGPoint currentPoint2 = [touch2 locationInView:nil];
    CGFloat currentAngle = atan2 (currentPoint2.y - currentPoint1.y, currentPoint2.x - currentPoint1.x);
    				
    transform = CGAffineTransformRotate(transform, currentAngle - previousAngle);
    self.view.transform = transform;
    Put this under touchesMoved method in your view controller.
    Cheers ;)
     
  7. macrumors 6502

    Joined:
    Mar 6, 2008
    Location:
    Melbourne, Australia
    #7
    I whacked that code into my TouchesMoved method, but im getting "transform undeclared. First use in this function" error message. I have never had to "Declare" any of my dozens of Translation Transforms before...

    Do I have to put "transform" anywhere else?


    Edit: I simply put "CGAffineTransform transform = ...." before "transform". It worked, but my View (A timetable) I want to flip with two fingers keeps vanishing? Do I have to import "Math.h"? And If so, how do I do it?

    Edit 2: A single swipe crashes the app in the simulator. I held Option to do a Two Fingered Rotate but it won't work still.
     
  8. macrumors newbie

    Joined:
    May 2, 2009
    #8
    I've implemented this code in a a view with an UIImageView as a subview. With the image in place it becomes really slow in the simulator, so I guess it will be even worse on the device.

    Is there a better way to implement this with images? Placing an image in a scroll view almost gives you the same functionality, but I understand it doesn't allow you to rotate?

    Edit: Maybe putting the image in a scroll view then using narut's rotate only code to rotate the scroll view will work better. I'll try that when I have some time.
     
  9. macrumors newbie

    Joined:
    May 20, 2009
    #9
    Move and rotate simultaneously with multi touch

    Hi,
    i need to move and rotate(simultaneously) an imageview with two touches,which will make it feel like a real object. i am able to both, but when i try to do it simultaneousl i get horrible output. I think i will have to figure out the angle and then decide whether to move or rotate it, but i am not able to do it. If any body has any idea,kindly post it.
    Thanks for reply
     

Share This Page