Sunday, December 6, 2009

A Better Two-Finger Rotate Gesture

A while back, I posted a mostly functional, but imperfect sample code for doing a two-finger rotate gesture. I've been meaning to revisit this for some time to get it working correctly.

Tonight, I finally found some time to do so, so I present a better, fully-functional two-finger rotate gesture sample code project. This version allows you to rotate 360° or more without problems. Rather than relying on the order of the touches and the overall rotation angle, I just calculate the angle between the fingers' current location and their previous location.

Screen shot 2009-12-06 at 10.05.04 PM.png
The new version is actually quite a bit simpler than the previous one. Since each instance of UITouch contains both its current location and its previous location, we don't even need to keep track of anything. The old version, in addition to not working correctly past 180° was working much harder than it needed to. The only new thing in this version is that the function that calculates the angle between the two lines looks at the slope of both lines and returns negative or positive values based on which slope is larger.



17 comments:

Joe Conway said...

This one is faster with the same effect, plus its a tad easier to scale the speed of the rotation: (Apologies for the formatting, won't let me use pre tag!)


static inline int ccw(CGPoint p0,
CGPoint p1,
CGPoint p2)
{
int dx1, dx2, dy1, dy2;

dx1 = p1.x - p0.x; dy1 = p1.y - p0.y;
dx2 = p2.x - p0.x; dy2 = p2.y - p0.y;

int v1 = dx1 * dy2;
int v2 = dy1 * dx2;

if (v1 > v2)
return 1;

if (v1 < v2)
return -1;

if ((dx1*dx2 < 0) || (dy1*dy2 < 0))
return -1;

if ((dx1*dx1 + dy1*dy1) < (dx2*dx2 + dy2*dy2))
return 1;

return 0;
}



- (void)touchesMoved:(NSSet *)touches
withEvent:(UIEvent *)event
{
static float coef = 0.1;

if ([touches count] == 2) {
NSArray *twoTouches = [touches allObjects];
UITouch *first = [twoTouches objectAtIndex:0];
UITouch *second = [twoTouches objectAtIndex:1];

CGPoint a = [first previousLocationInView:[self view]];
CGPoint b = [second previousLocationInView:[self view]];
CGPoint c = [first locationInView:[self view]];

int direction = ccw(b, a, c);
[label setTransform:CGAffineTransformRotate([label transform], direction * coef)];
}
}

Jeff LaMarche said...

Joe:

Thanks for sharing - this looks like a neat approach, doubt it would have occurred to me. I'll have to sit down and read through it when I have some time to digest it.

Doesn't this ignore the speed at which the user's fingers rotate? Seems like it creates a straight-line rotation regardless of how fast the user actually moves their fingers? Or, is the frequency at which touchesMoved:withEvent: is called affected by the speed?

Donald W. Miller, Jr. said...

I'm at a loss to come up with a scenario where you actually really need TWO fingers to do a rotation (e.g. of a knob or dial). Just one touch would do it (I think) with the illusion that the second touch actually does anything. I have had a one-trick pony app out there for a while (The Wheel RE - which was really a joke and now brings in enough for expresso every day) and I'm sure folks use two fingers but I only track one touch (and I used all the dot-product, cross-product and trig functions)

Jeff LaMarche said...

Donald:

Interesting - may I ask how you determine which finger to use? For users who rotate around one finger, likely some will rotate around the first finger, and others will rotate around the second.

Not doubting that your solution works, but I try to post the most generic solutions I can, since I can't predict what people will be using the code for if they borrow it.

Donald W. Miller, Jr. said...

I actually have multi-touch off (it's the default I believe), so it's the first finger to touch within the view (i.e. the radius of the circular image because I don't want any touches in the corners of the view rect outside the circular image). So, it does not matter which touch is used as long as it stays within the image bounds (virtual). For a large circular image, the closer the touch gets to the center, it REALLY spins (like a real wheel).

Jeff LaMarche said...

Donald:

Interesting approach, I hadn't thought of doing it that way, but for a lot of applications, I could see that working well - thanks for clarifying.

Joe Conway said...

Yes, the dot product approach just checks to see if the motion was clockwise or counter-clockwise. The frequency at which touchesMoved:withEvent: is sent makes these two methods perform pretty much the same. In some cases (especially games, where most of the app's time is spent rendering), having a normalized direction of the rotation is useful for making animation appear more smoothly.

Sara Reid said...

I'm working on an iPhone app with a lot of different gesture inputs that you can do. Currently there is single finger select / drag, two finger scroll, and two finger pinch zoom-in / zoom-out. I want to add in two finger rotation, but I can't figure out how to get it to work right.

accessoires

chandrabhan said...

I’d been taught that left-aligned labels are preferred, to support the prototypical F-shaped eye-tracking heatmap of web browsing. The idea is that it supports easy vertical scanning.


IT developer

--@@*** DAVID ***@@-- said...

Hi,

The fully Functional two-finger rotate gesture is really simple and useful to implement for me as a beginner.
Thank you very much.
I've used it with an ImageView and it works PERFECTLY.

Do you think it could be possible modify the code to rotate AND ALSO ZOOM/scale the imageView??

I mean that if you simple use rotation, then the image only rotate...but if you reduce the distance between the two fingers the imageView should scale...

Any chance to post a code for that???
Thanks again!!!

David

Berko said...

The project download launches and runs just fine for me, but when I try to apply this to another project, it doesn't work. I even created a new project with the same name and everything, and copied in the code exactly as yours, but it's still not working...only the original download works. Any suggestions as to what I may be missing? I'm sure it's something stupid.

Muhammad said...

Thanks for sharing Jeff. This really helped for a beginner like me. One question btw. What changes are we supposed to make if we are to implement this in a simple opengl es application?

Koddy said...

The Tow-finger rotate is so awesome! It can be made in different ways. Somethings that I think is also fabulous is viagra online that won't let you down.

javieth said...

This blog is really interesting!! i really loved it, i think all the new ideas written in this blog is encouraged me to continue the reading this blog. simply wonderful.

buy viagra

katty said...

I love to read interesting blogs, because of that i prefer to read blogs like this.Recently i saw a blog about costa rica investment opportunities think it was wonderful too.

Edwin said...

scrub m65 kamagra attorney lawyer body scrub field jacket lovegra marijuana attorney injury lawyer

h4ns said...

What youre saying is completely true. I know that everybody must say the same thing, but I just think that you put it in a way that everyone can understand. I also love the images you put in here. They fit so well with what youre trying to say. Im sure youll reach so many people with what youve got to say.

Arsenal vs Huddersfield Town live streaming
Arsenal vs Huddersfield Town live streaming
Wolverhampton Wanderers vs Stoke City Live Streaming
Wolverhampton Wanderers vs Stoke City Live Streaming
Notts County vs Manchester City Live Streaming
Notts County vs Manchester City Live Streaming
Bologna vs AS Roma Live Streaming
Bologna vs AS Roma Live Streaming
Juventus vs Udinese Live Streaming
Juventus vs Udinese Live Streaming
Napoli vs Sampdoria Live Streaming
Napoli vs Sampdoria Live Streaming
Fulham vs Tottenham Hotspur Live Streaming
Fulham vs Tottenham Hotspur Live Streaming
AS Monaco vs Marseille Live Streaming
AS Monaco vs Marseille Live Streaming
Alajuelense vs Perez Zeledon Live Streaming
Alajuelense vs Perez Zeledon Live Streaming
Technology News | News Today | Live Streaming TV Channels