2

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.

enter image description here

The three problems that I can't seem to figure out are;

  1. How can I restrict user to rotate only in clockwise direction? Currently user can move it in any direction (clockwise and counter clockwise)

  2. 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.

  3. 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?

Bo A
  • 3,144
  • 2
  • 33
  • 49
Sam B
  • 27,273
  • 15
  • 84
  • 121
  • 1
    Incidentally, on the standard US non-compact rotary phone the digits are positioned in increments of 1/12 of a circle, leaving 2 digit positions empty at the start of the rotation. – Hot Licks Dec 09 '12 at 23:03
  • 1
    @Sam Budda Can you please provide me this code what you are talking about in question above. I need to use the same functionality. – iPhone Programmatically Dec 09 '13 at 13:40

5 Answers5

1

Let’s assume that you’re talking about this gesture:

Rotary Dial 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.

  1. 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.
  2. 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.
  3. 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.

Evadne Wu
  • 3,190
  • 1
  • 25
  • 25
0

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.

martin's
  • 3,853
  • 6
  • 32
  • 58
  • sorry dude, this is way more complicated than I need it to be. I know there are hundreds of way to skin a cat and this is just one way. – Sam B Dec 09 '12 at 19:10
  • This is fairly simple stuff, actually. But, sure, you can do it a million different ways. And that's OK. – martin's Dec 10 '12 at 22:31
0

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 :-) )

Peter M
  • 7,309
  • 3
  • 50
  • 91
0

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.

Thunk
  • 4,099
  • 7
  • 28
  • 47
  • When animating a rotation angle greater than pi you need to do it in two steps, or the rotation will go backwards. – Hot Licks Dec 09 '12 at 23:01
  • Rotation angle is giving me a lot of grief please check another question of mine that has sample code and see if you can figure it out http://stackoverflow.com/questions/13791582/cgaffinetransformmakerotation-counter-clockwise-always – Sam B Dec 10 '12 at 06:09
0

Here is a better dial:

enter image description here

Have fun!

Hot Licks
  • 47,103
  • 17
  • 93
  • 151