Implementing touch rotation in cocoa touch

I am wondering what is the best way to implement rotation-based drag and drop movements in my iPhone application.

I have a UIView that I want to rotate around its center when the user's fingers touch the view and they move it. Think of it as a dial that you need to adjust with your finger.

The main question is as follows:

1) Do I have to remember the starting angle and convert when Began is called, and then every time I touch the mod is called applying a new transform to a representation based on the current position of the finger, for example, something like:

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { UITouch *touch = [touches anyObject]; CGPoint currentPoint = [touch locationInView:self]; //current position of touch if (([touch view] == self) && [Utility getDistance:currentPoint toPoint:self.middle] <= ROTATE_RADIUS //middle is centre of view && [Utility getDistance:currentPoint toPoint:self.middle] >= MOVE_RADIUS) { //will be rotation gesture //remember state of view at beginning of touch CGPoint top = CGPointMake(self.middle.x, 0); self.initialTouch = currentPoint; self.initialAngle = angleBetweenLines(self.middle, top, self.middle, currentPoint); self.initialTransform = self.transform; } } - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{ UITouch *touch = [touches anyObject]; CGPoint currentPoint = [touch locationInView:self]; //current position of touch if (([touch view] == self) && [Utility getDistance:currentPoint toPoint:self.middle] <= ROTATE_RADIUS && [Utility getDistance:currentPoint toPoint:self.middle] >= MOVE_RADIUS) { //a rotation gesture //rotate tile float newAngle = angleBetweenLines(self.middle, CGPointMake(self.middle.x, 0), self.middle, currentPoint); //touch angle float angleDif = newAngle - self.initialAngle; //work out dif between angle at beginning of touch and now. CGAffineTransform newTransform = CGAffineTransformRotate(self.initialTransform, angleDif); //create new transform self.transform = newTransform; //apply transform. } 

OR

2) Should I just remember the last known position / angle and rotate the view based on the difference in angle between this and now, for example:

 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { UITouch *touch = [touches anyObject]; CGPoint currentPoint = [touch locationInView:self]; //current position of touch if (([touch view] == self) && [Utility getDistance:currentPoint toPoint:self.middle] <= ROTATE_RADIUS && [Utility getDistance:currentPoint toPoint:self.middle] >= MOVE_RADIUS) { //will be rotation gesture //remember state of view at beginning of touch CGPoint top = CGPointMake(self.middle.x, 0); self.lastTouch = currentPoint; self.lastAngle = angleBetweenLines(self.middle, top, self.middle, currentPoint); } } - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{ UITouch *touch = [touches anyObject]; CGPoint currentPoint = [touch locationInView:self]; //current position of touch if (([touch view] == self) && [Utility getDistance:currentPoint toPoint:middle] <= ROTATE_RADIUS && [Utility getDistance:currentPoint toPoint:middle] >= MOVE_RADIUS) { //a rotation gesture //rotate tile float newAngle = angleBetweenLines(self.middle, CGPointMake(self.middle.x, 0), self.middle, currentPoint); //touch angle float angleDif = newAngle - self.lastAngle; //work out dif between angle at beginning of touch and now. CGAffineTransform newTransform = CGAffineTransformRotate(self.transform, angleDif); //create new transform self.transform = newTransform; //apply transform. self.lastTouch = currentPoint; self.lastAngle = newAngle; } 

The second option makes more sense to me, but it does not give very pleasant results (uneven updates and minor rotations). Which way is better (if any), in terms of performance?

Hurrah!

+4
source share
2 answers

Actually it is much easier than you tried.

You will need three data points:

  • The origin of your presentation.
  • Current Touch Location
  • Location of previous touch

The Touch object passed to you actually contains the last touch location. Therefore, you do not need to track this.

All you have to do is calculate the angle between the two lines:

  • Origin to the current touch
  • Origin before previous touch

Then convert this to radians and use it in CGAffineTransformRotate (). Do it all at your touch. The handler has been processed.

Here is a function to calculate what you need exactly this:

 static inline CGFloat angleBetweenLinesInRadians(CGPoint line1Start, CGPoint line1End, CGPoint line2Start, CGPoint line2End) { CGFloat a = line1End.x - line1Start.x; CGFloat b = line1End.y - line1Start.y; CGFloat c = line2End.x - line2Start.x; CGFloat d = line2End.y - line2Start.y; CGFloat line1Slope = (line1End.y - line1Start.y) / (line1End.x - line1Start.x); CGFloat line2Slope = (line2End.y - line2Start.y) / (line2End.x - line2Start.x); CGFloat degs = acosf(((a*c) + (b*d)) / ((sqrt(a*a + b*b)) * (sqrt(c*c + d*d)))); return (line2Slope > line1Slope) ? degs : -degs; } 

Courtesy of Jeff LaMarche at:

http://iphonedevelopment.blogspot.com/2009/12/better-two-finger-rotate-gesture.html

Example:

 UITouch *touch = [touches anyObject]; CGPoint origin = [view center]; CGFloat angle = angleBetweenLinesInRadians(origin, [touch previousLocationInView:self.superview.superview], origin, [touch locationInView:self.superview.superview]); 
+3
source

Do you consider using a UIRotationGestureRecognizer ? It seems that this logic has already baked, and can make things easier.

0
source

Source: https://habr.com/ru/post/1308986/


All Articles