(Sample code on GitHub )
In fact, it is not so difficult, there is simply a lot of trigonometry.
Now, what I will describe now is not animation , since you asked him to track the position of the finger in the title. The animation will include its own synchronization function, but since you use a touch gesture, we can use the inherent timings that this event has, and simply rotate the view accordingly. (TL; DR: user saves travel time, not hidden timer).
Finger tracking
First of all, let it define a convenient class that tracks the angle, I will call it DialView . This is really just a subclass of UIView that has the following property:
DialView.h
@interface DialView : UIView @property (nonatomic,assign) CGFloat angle; @end
DialView.m
- (void)setAngle:(CGFloat)angle { _angle = angle; self.transform = CGAffineTransformMakeRotation(angle); }
UIButton may be contained in this view (I'm not sure if you want the buttons to be responsible for the rotation? I'm going to use UIPanGestureRecognizer , since this is the most convenient way).
Let's build a view controller that will handle the panorama gesture inside our DialView , let it also save a link to DialView.
Myviewcontroller.h
@class DialView; @interface ViewController : UIViewController // The previously defined dial view @property (nonatomic,weak) IBOutlet DialView *dial; // UIPanGesture selector method - (IBAction)didReceiveSpinPanGesture:(UIPanGestureRecognizer*)gesture; @end
It depends on how you connect the panorama gesture, personally, I did it on the nib file. Now the main part of this function:
Myviewcontroller.m
- (IBAction)didReceiveSpinPanGesture:(UIPanGestureRecognizer*)gesture {
CGPointAngle is a method invented by me, it's just a nice wrapper for atan2 (and I CGPointDistance if you call NOW!):
CGFloat CGPointAngle(CGPoint a, CGPoint b) { return atan2(ay - by, ax - bx); } CGFloat CGPointDistance(CGPoint a, CGPoint b) { return sqrt(pow((ax - bx), 2) + pow((ay - by), 2)); }
The key here is that there are two angles for tracking:
- The angle of the view itself
- The angle between the finger and the center of view.
In this case, we want the viewing angle to be originalAngle + deltaFinger , and that what the above code does, I just encapsulate all the state in a struct.
Radius check
If you want to track the "presentation border", you should use the CGPointDistance method and check if the distance between begin.center and now.finger specific value.
This is homework for ya!
Snap back
So this part is the actual animation, since the user no longer controls it.
Snapping can be achieved using a set defined by the corners, and when the finger releases the finger (gesture has ended), snap to them as follows:
else if (gesture.state == UIGestureRecognizerStateEnded) { // Number of "buttons" NSInteger buttons = 8; // Angle between buttons CGFloat angleDistance = M_PI*2 / buttons; // Get the closest angle CGFloat closest = round(self.dial.angle / angleDistance) * angleDistance; [UIView animateWithDuration:0.15 animations:^{ self.dial.angle = closest; }]; }
The buttons variable is just a reserve for the number of buttons that are in the view. The equation is super simple really, it just abuses the rounding function to get closer to the closing angle.