Spinning animation following a finger from uibutton that follows the outer circle track

I'm looking for a little guide to start figuring out an animation that tracks finger movement and moves the UIButtons collection along the outer circle path.

enter image description here

I imagine it will be like a revolver. How everyone locks in place below

or as scrolling through one of these slide inserts

enter image description here

early

+4
source share
1 answer

(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 { // This struct encapsulates the state of the gesture struct state { CGPoint touch; // Current touch position CGFloat angle; // Angle of the view CGFloat touchAngle; // Angle between the finger and the view CGPoint center; // Center of the view }; // Static variable to record the beginning state // (alternatively, use a @property or an _ivar) static struct state begin; CGPoint touch = [gesture locationInView:nil]; if (gesture.state == UIGestureRecognizerStateBegan) { begin.touch = touch; begin.angle = self.dial.angle; begin.center = self.dial.center; begin.touchAngle = CGPointAngle(begin.touch, begin.center); } else if (gesture.state == UIGestureRecognizerStateChanged) { struct state now; now.touch = touch; now.center = begin.center; // Get the current angle between the finger and the center now.touchAngle = CGPointAngle(now.touch, now.center); // The angle of the view shall be the original angle of the view // plus or minus the difference between the two touch angles now.angle = begin.angle - (begin.touchAngle - now.touchAngle); self.dial.angle = now.angle; } else if (gesture.state == UIGestureRecognizerStateEnded) { // (To be Continued ...) } } 

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.

+5
source

Source: https://habr.com/ru/post/1494176/


All Articles