views:

154

answers:

4

Okay so I'm making something that basically works as a knob, but only half on the screen -- so I just need horizontal swipes to cause it to rotate. I have it nearly all sorted with one exception: if you change direction of your swipe in mid-swipe, the rotation doesn't change direction. I even can see the problem, but not sure what to do about a solution.

So in my touchesMoved, I get the swipe into radians in the predictable way:

CGFloat radians = atan2f(location.y - centerY, location.x - centerX);

I then store radians, add/subtract it to previous rotation and then give the result to the CATransform3D.

So the prob is that even though the swipe changes direction, there is a "balance" which doesn't allow an immediate change of direction.

Does this make any sense?

A: 

i did something similar to this for simulating a turntable scratching motion. Try:

            [CATransaction begin];
            [CATransaction setValue:[NSNumber numberWithInt:0] forKey:kCATransactionAnimationDuration];
            [myLayer setValue:[NSNumber numberWithFloat:radians] forKeyPath:@"transform.rotation.z"];           
            [CATransaction commit]; 

on the underlying layer of your view.

Remover
+1  A: 

First, I hope you get that atan() returns values in the range -π/2..+π/2, so you'll never get values in the other half of the circle. (If you deduce that the answer is on that side, you have to do your own flipping/mirroring.)

In your touches-moved, NSLog() what you've calculated the new difference to be, and be sure that it's the value you're expecting.

Of course, you keep the previous radians on touchesBegan, and save-off the current radians on init, touchesEnded and touchesCancelled, right?

(In many cases, touchesCancelled can just fwd the arguments to touchesEnded.)

If you do all of that, then the scheme you describe ought to work. If it does not, perhaps provide more information, like the NSLog() output for the various values when you think those values should be different.

Olie
+1  A: 

For interactions like this, what I find usually works best is to save the state at the start of the interaction and the touch down location. Then, whenever you update, compute the new state based on the distance between the original touch and the current touch (applied as a difference from the original state).

For example, in the case you mention, what I would do on a touch down would be to store the current angle of the knob and the location of the touch. Then, on a touch move, I would compute the difference between the current touch location and the starting location. This will give you a delta value, which you can translate into an angle which you can add to the starting angle - update your knob graphic accordingly. This ought to allow you to change direction however you like throughout your behavior without worrying about keeping up with incremental changes to your angle. This also makes it extremely easy to handle things like resistance or dampening after a certain angle - far easier than it would be to handle that when updating things incrementally.

cc
+1  A: 

Be careful to use atan2 to avoid quadrant issues and division by zero. That's what it's there for.

// angle with +ve x-axis, in the range [0, 2π)
float getDirection(CGPoint V) 
{
    double theta = atan2(V.x, V.y); // angle with +ve x-axis, in the range (−π, π]
    if (theta < 0)
        theta = 2 * M_PI - theta;
    return theta;
}

+ (CGPoint) vectorFrom: (CGPoint) A to: (CGPoint) B 
{ return CGPointMake(B.x - A.x, B.y - A.y); }
Ohmu
+1 for the nice tip -- I'd forgotten about atan2() :)
Olie