rotary dial - rotation limitation - iphone

I am making a vintage phone and got a working starting code where user moves his fingers over a UIImageView numbers and it rotates dial. It then moves it back to original position. See screenshot.
The three problems that I can't seem to figure out are;
How can I restrict user to rotate only in clockwise direction? Currently user can move it in any direction (clockwise and counter clockwise)
How can I detect which number that user selected? Meaning user touched 1 or 3 or 5? I need this info so that I can stop the rotation when that number reaches the bar on the right.
In my current code when I stop the rotation and let go of the circle, it moves back to it's place by moving back counter clockwise. It works well if I select 1,2,3,4 but for any number 5 and up the dial moves clockwise back to its original position. How can I force counter clockwise motion on touchesEnded?

Let’s assume that you’re talking about this gesture:
Source.
Build a single-touch rotation gesture recognizer. After building the gesture recognizer correctly, you can just look at the rotation and see what to do with the rotary pad.
There are several things you’ll consider when building a single-touch rotation gesture recognizer. If you look at UIRotationGestureRecognizer, it uses connection between two touches, backed by two fingers, to derive the current angle, then compares the angle to the previous angle, derived from an earlier touch change event, to see the delta.
Measuring the current angle
It takes two points to form a line and you need a line to know the angle. If you’re working with only one touch, you need an anchor point. There are many ways to send an anchor point to your gesture recognizer, and since you’re likely going to build a custom class, use delegation.
Accumulating rotation counts
If you simply note the angle and send off messages during touch changes, it’ll sometimes work. However, if you’d like to implement hysteresis (e.g. this rotary dial will only rotate once clockwise, then it tightens up), you’ll need to accumulate rotation counts for both clockwise and counter-clockwise directions.
Fortunately, you can assume that a) the touch events will not get dropped too often, and b) simply comparing the current angle against the past angle, seeing if they cross quadrant boundaries, will suffice.
For example:
If the touch moved from the top-left quadrant into the top-right quadrant, add one to the rotation count.
If the touch moved from the top-right quadrant into the top-left quadrant, subtract one from the rotation count.
(Yup, this actually works.)
Emitting the correct, accumulated rotation
If you want to emit rotation information exactly like how UIRotationGestureRecognizer did, there will be four things you’re tracking.
Starting Angle: The angle between a connection from the anchor point to the starting touch, and a connection from the anchor point to a fixed reference point.
Current Angle: The angle between a connection from the anchor point to the current touch, and a connection from the anchor point to a fixed reference point.
Rotation Count: The number of clockwise revolutions derived from continuously comparing the current value of Current Angle against its last value (as talked about in the last section). If the touch is moving counter-clockwise, then this count will go into negative.
You’ll provide Rotation Count * 2_PI + (Current Angle - Starting Angle) as the rotation.

OK, I would take a different approach. First, you want to create a RotaryDial class to encapsulate all of the behavior. Then you can just plug it into any view as you see fit.
To keep things simple I would consider making each number button a movable UIImageView, call it RotaryDialDigit or something like that. You would instantiate and place ten of those.
The dial "frame" would just tag along for the ride as the user moves one of the RotaryDialDigit buttons. It's just an image (unless you want the user to be able to touch it and do something with it.
From there, knowing which button is being held down and limiting its rotation to a given direction as well as stopping at at the bar is fairly easy stuff.
By using a protocol you can then have the RotaryDial instance tell the container when a number has been dialed. To the container RotaryDial would feel like a keypad sending a message every time a button is pressed. You really don't want the container bothering with anything other than completed number selections.

To detect which number is touched, when you create each number you should set the tag value of its UIView. Then when the user touches the number you can detect which UIView object it was by checking that tag value.
For the rotation problem, I'd suggest looking at how you are calculating the angle. At a guess I'd say for numbers greater than 4 (which you discern from the tag) you need to do something like subtract the angle you are currently calculating from 360 degrees (well 2Pi). (But I have a head cold right now so the actual math is escaping me :-) )

Without seeing your code, I assume the numbers are a static image and you are animating the finger holes as they rotate past each number. If so:
Detecting which number: defina a CGRect around each button. When the user taps the screen, check which rectangle contains the tap location.
Controlling rotation direction: as the user drags their finger, comtinuously calculate the angle from the dial stop to the current tap location. If the angle moves in the wrong direction, dont update the position of the finger hole. Note that trig functions return vales from +Pi to -Pi radians. For the digits greater than 5, rather than handle negative angles you will probably want to add 2Pi radians ( or 360 degrees) to the angle.
Rotating wrong way: the digits below 5 are generatting angles in the range of 0 to -Pi. Without seeing code, I suspect adding 2Pi to the angle will correct your rotation direction.

Here is a better dial:
Have fun!

Related

SpriteKit move node with physics rather than updating position

My main character moves by touching and holding him and then moving your finger left or right to move the character. To do this I just simply update the node's x position (walks left and right on a flat surface) in touchesMoved() with the x position of the touch location, and apply an animation depending on the direction he's moving.
I want to kind of take this to the next level and accomplish the same effect, but using physics, so that when I'm done moving him and release my finger, he may slide a little bit in the direction he was moving given the speed I was moving him at, if that makes sense. Does anyone know how I can accomplish this effect?
Would I have to do as I'm currently doing and update the position as it moves, but also apply a force/impulse at the same time? Kind of confused on how to approach this
Moving the physics body via force, impulse, or velocity will automatically update the player position.
So you will have to play around with the correct way to accomplish your goal. But based on this, what I would suggest is replace your .position code with .physicsBody!.velocity code in your touchesMoved. Then, on your touchesEnded, you can apply a bit of an impulse to give the player that little bit of an "on ice" effect to keep them going a tad.
You will need to experiment with how much velocity you want to get the character to move at the correct speed, and then you will need to play with the impulse figures as well to get it just right.
This could get a bit tricky though, in touchesMoved... because at some point you will want to reset the velocity to 0 (so they stop moving when your finger stops).
To do that, you will need to to use the .previousLocation from your touch object, and compare the distance of X moved. if the distance X moved is >0 (or some deadzone threshold) then apply the velocity; if the deltaX is 0, then set the velocity to 0.
This may actually be more complicated than just using .position to move the character, then having the character slide a bit with physics on touchesEnded.
You will have to play with it to get it right.

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 detect a circle motion with UIGestureRecognizer

I want to be able to detect someone's finger drawing a circular motion on the screen - as if they were drawing an 'O'. Is this possible with UIGestureRecognizer?
I think the answer to this depends on your definition of circular motion and how you intend to use it. For example, do you want to know how many degrees along a circle the users finger has travelled? Or, do you only care about a circle being completed? What is the degree of accuracy you require? Do you want to allow for the motion to be interrupted or does this have to be more of a touch-down > draw-circle > touch-up (in other words, single motion)?
One approach would be to define a bunch of rectangular zones along the circumference and detect if the user is touching these in sequence. This can provide you with direction and a coarse indication of angle.
Another approach is to store the points between touch down and touch up and do some filtering and curve fitting to figure out what shape is approximated by the points. First low-pass-filter using a basic FIR filter and then look at the dx and dy from point to point. A circle (as a series of arcs) will have to fall within a certain range of slope changes from point to point, otherwise you have some other shape.
Yet another approach is to use a Neural Network to take the points and tell you what the shape looks like.
I think this may be what you need
How to detect circular gesture via Gesture Recognizer?
Instead using a gesture recognizer, this project reacts to circular motions tracking the angle of UITouch events.
My answer to my question:
I used this: http://iphonedevelopment.blogspot.com/2009/04/detecting-circle-gesture.html
.. but turned the CircleView into a custom UIGestureRecognizer. Everything lovely.
No, it doesn't recognize natively a circular motion.
You have to implement your own method to do that.
Here's how i needed to do it using the touches callbacks in my view controller but this could be made into a gesture too. Note, I was trying to detect multiple circle motions (2 or more clockwise or counterclockwise circles made during a touch event.
Store touchesMoved CGPoints in an array.
Create a min/max rect of all the points in your history array.
Divide this min/max rect into 4 smaller rects.
Assign each history point a quadrant using CGRectContainsPoint() for each of the 4 quadrants
A clockwise motion will have quadrants ascending. A counter-clockwise motion will have quadrants descending.
Check the ratio of width/height if you want to detect circles vs ovals

Drag object with touchesMoved along offset vector / delta?

In the touchesMoved method, what is the math to have an object on the screen move along a dragging touch vector? For instance, if I touch beneath my object (Y > object.y), and drag "up", I want the object to move from its current x,y location along the same vector that my finger moves.
Currently I have it working that the drag motion snaps my object to my finger's location, but sometimes my finger hits the edge of the screen before I get my object positioned correctly.
If I know my "touch down location" and the "current touch location" what is the math to determine where to move object.position?
I hope that you have already received the answer of figured it out yourself, but.
Assume that your object's Upper Left Corner (ULC) is at Object.X, ObjectY.
Assume that the CGPoint returned by touchsBegan is Touch.X, Touch.Y.
The offset (Delta) will be:
Delta.X = Touch.X - Object.X
Delta.Y = Touch.Y - Object.X
Subtract these from each point returned by touchesMoved and your ULC should move as you expect rather than snapping to your finger.
Good luck.
I would look into UIPanGestureRecognizer. It's what I use when I want to accomplish something like what you are describing.
This is what I used to learn about them but then took it further in other forms:
http://www.cocoanetics.com/2010/11/draggable-buttons-labels/
Good luck!

iphone cocoa : how to drag an image along a path

I am trying to figure out how can you drag an image while constraining its movement along a certain path.
I tried several tricks including animation along a path, but couldn't get the animation to play and pause and play backwards - so that seems out of the question.
Any ideas ? anyone ?
What you're basically trying to do is match finger movement to a 'translation' transition.
As the user touches down and starts to move their finger you want to use the current touch point value to create a translation transform which you apply to your UIImageView. Here's how you would do it:
On touch down, save the imageview's starting x,y position.
On move, calculate the delta from old point to new one. This is where you can clamp the values. So you can ignore, say, the y change and only use the x deltas. This means that the image will only move left to right. If you ignore the x and use y, then it only moves up and down.
Once you have the 'new' calculated/clamped x,y values, use it to create a new transform using CGAffineTransformMakeTranslation(x, y). Assign this transform to the UIImageView. The image moves to that place.
Once the finger lifts, figure out the delta from the original starting x,y, point and the lift-off point, then adjust the ImageView's bounds and reset the transform to CGAffineTransformIdentity. This doesn't move the object, but it sets it so subsequent accesses to the ImageView use the actual position and don't have to keep adjusting for transforms.
Moving along on a grid is easy too. Just round out the x,y values in step 2 so they're a multiple of the grid size (i.e. round out to every 10 pixel) before you pass it on to make the translation transform.
If you want to make it extra smooth, surround the code where you assign the transition with UIView animation blocks. Mess around with the easing and timing settings. The image should drag behind a bit but smoothly 'rubber-band' from one touch point to the next.
See this Sample Code : Move Me