Radial Dial in Xcode - iphone

I am trying to make an app that uses a radial dial. As an example the radial dial features as availabel in this app
http://itunes.apple.com/us/app/kitchen-dial/id448558628?mt=8&ign-mpt=uo%3D4
What I wanted to ask is what method should I use to accomplish this? Guys, I am not asking you to write the code for me what I am asking is how is this done conceptually? I mean when a user swipses left/right on the screen how does it know which radial dial to move and in which direction. I am sure there are multiple buttons and there is some image transition going on here. I need to understand the concept of making such a thing. If anyone know or has done something similar please do share your thoughts. Does apple has a sample code for this? Thank you.

The central concepts are:
Ability to rotate an image around a center point.
Ability to calculate an angle relative to the center point given an x,y coordinate.
So, to move the wheel:
On touch began:
Calculate starting angle based on x,y
On touch moved:
Calculate new angle based on x,y and subtract starting angle. This is the angle delta (or how much more to rotate the image by).
To register button taps on the wheel:
On tap:
Calculate angle based on x,y, add existing rotation, divide by the angle size of each segment to get an index.
You asked for a high-level explanation of the concepts - and that's pretty much it.

This is a complicated question. The simple answer is: use a pan gesture recognizer, with:
- (void)panBegan:(UIPanGestureRecognizer *)pan {
CGRect frame = [self frame];
_central = CGPointMake(frame.origin.x+frame.size.width/2, frame.origin.y+frame.size.height/2);
CGPoint p = [pan locationInView:[self superview]];
p.x -= _central.x;
p.y -= _central.y;
_angle = atan2(p.x, p.y);
}
- (void)panMoved:(UIPanGestureRecognizer *)pan {
CGPoint p = [pan locationInView:[self superview]]];
p.x -= _central.x;
p.y -= _central.y;
CGFloat deltaAngle = _angle - atan2(p.x, p.y);
self.transform = CGAffineTransformMakeRotation(deltaAngle + _finalAngle);
}
where _angle is angle of the initial touch of the pan, and _finalAngle is saved angle at the end of the previous pan.
But: the UIPanGestureRecognizer has a velocity property, and you should be using it at the end of the pan to continue the rotation, as if the dial had inertia. And, if the dial has a fixed integer set of legal stopping positions, then you must adjust the slowdown curve so it stops on a legal stop. And, if you animate the deceleration, and the user starts a new pan before the previous one is finished, then you have to adjust _finalAngle based on the perceived position, not the stored position.

Here is a list of rotary knobs that are open source, so you can look at the code and see whats involved:
http://maniacdev.com/2011/12/open-source-libraries-for-easily-adding-rotary-knob-controls-in-your-ios-apps/

Related

Rotating a rectangle using a UIPanGestureRecognizer

I have the following view where I'm using a pan gesture in the upwards or downwards direction to rotate it positively or negatively:
I'm wondering, is there a mathematical equation to precisely covert the amount panned to the amount it should be rotated so the timing is correct to keep the users finger on the view while it is rotating? For instance, if the pan translation comes back as 1, what would the proper amount be to rotate it?.
There are a few details you need to provide to give a meaningful answer:
Are you rotating the view about its centre (the default) or is there an anchor point?
Since the view is rotating, while the touch is moving strictly vertically in the superview, what's the expected behaviour as the view rotates further away from the vertical line defining the pan?
Is there a reason you're using a pan gesture instead of a rotation gesture, or even just direct touch tracking? It seems like it creates more problems than it solves.
I'm going to assume the view is rotating about its centre for the sake of simplicity, and I'll use a pan starting on the right side of the view as an example, with the rotation not exceeding ±90°. Here are two options:
Movement up and down translates linearly to the angle of rotation, i.e., a pan of a given distance rotates the view the same amount, no matter where the pan starts. In that case, you need to decide what the top and bottom limits of the pan are. They might be the bounds of the superview. Regardless, you want to convert the distance travelled in the Y direction to a value between -1 and 1, where -1 represents the bottom limit and 1 represents the top limit. Something like 2 * (dy / superview.bounds.size.height - 0.5). Multiply that by π/2 (M_PI_2 in math.h) to scale from the range [-1, 1] to the range [-π/2, π/2] and you've got the angle to add/subtract from the view's rotation at the beginning of the gesture.
The view tracks the touch so that its right edge is always "pointed at" the touch. In this case, pan isn't terribly useful because you only need the location of the touch in the superview, not the distance travelled. Calculate dx and dy as the difference in x and y coordinates from the view's centre to the touch location. Then calculate atan2(dy, dx) and you've got the absolute angle of rotation for the view.
I hope this puts you on the right track.
The answer is the angle would be panAmount.y / rectangleWidth.
Here is proof: https://math.stackexchange.com/questions/322694/angle-of-rotated-line-segment

How to Plot (show) user location on Image? iOS

So here is task i need to get done on iOS:
Show User's Current Location on an Image like this:
Yes, like this Image!
So few things one will notice as soon as one look at it. Its Tilted. It's headings are Off. You can see horizon..!
Lets say,
I have location coordinates for corners of this image.
I have angle at which this image is tilted. (e.g 50°)
I have heading angle of the image. (e.g -170° North, which means its about 10° South-West, correct me if im wrong.)
And i have Zoom level on which this image would have been zoomed if it was on Google Maps/Google Earth. (e.g 14 zoom level - out of 20)
But thats the task, i need to show users location on such images. Just like in "Google Earth".
Can anyone give me heads up to accomplish this task?
Thank you everyone. :)
Given the information you have, sounds like you can use line-plane intersection to solve your problem.
I achieved the desired result by plotting the coordinate on a UIView and transform it using CATrasnform3D. These explanations also help, Explanation of Transformation Matrix in terms of Core Animation and Core Animation Programming Guide itself.
_graphic.transform = CGAffineTransformMakeRotation(-2.89724656);
CATransform3D rotatedTransform = _graphic.layer.transform;
rotatedTransform.m34 = 1.0 / 500;
rotatedTransform.m22 = -2;
rotatedTransform.m41 = 170;
rotatedTransform.m42 = 170;
rotatedTransform =
CATransform3DRotate(rotatedTransform,
51 * M_PI / 180.0f,
1.0f,
0.0f,
0.0f);
_graphic.layer.transform = rotatedTransform;
[_graphic setNeedsDisplay];
Thank you all.

How to create a spinnable image for iPhone/iPad?

I am really at a loss for starting this project. I have an object that I want the user to be able to flick their finger and spin on an iPhone/iPad app that I am creating, but don't really know how to do it. As an example of what I am talking about, the Elements app allows you to "spin" the element. Any thoughts?
The Elements uses OpenGL to present a 3D model. I suggest you look into OpenGL if you want a similar effect.
http://maniacdev.com/2009/04/8-great-resources-for-learning-iphone-opengl-es/
In the case of The Elements, it's not an image you're spinning, it's a 3D model.
This will spin something in a circle. However, you'll have to handle your touch events for the flick
Where angle is a CGFloat and theView is a UIView
-(void)spinViewABit
{
angle += 0.01;
if (angle > 6.283) {
angle = 0;
}
CGAffineTransform transform=CGAffineTransformMakeRotation(angle);
theView.transform = transform;
}

Wheel rotates and goes back to starting position iPhone

I am using
float theAngle = atan2( location.y-self.center.y, location.x-self.center.x );
to rotate a wheel at a certain angle.
and
self.transform = CGAffineTransformMakeRotation(angleRadians);
for the transformation to take place.
But everytime the user represses the wheel to turn it, it goes back to the original location and starts from there. How may I stop this from happening please?
Thanks!
You may setting the rotation angle value of the wheel rather than adding the value to the current angle.
I would hazzard a guess that the reason is that you are calculating based on the original position of the wheel rather than the previous position of the wheel.
i.e. Presuming that self.center represents the original position of the wheel as first displayed, location represents the users desired new rotation and looking up atan2 on wikipedia (trig is not my strong point :-) I would guess you need self.location to be updated each time to the be location so that the next rotation starts from the last rotation.
Just a guess :-)

rotating circle with swipe question

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?
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.
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.
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); }
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.