ios animation uibutton core-animation

ios - Animación giratoria, que sigue el dedo, de uibutones que sigue el camino exterior del círculo



animation uibutton (1)

Estoy buscando una pequeña guía para comenzar a descubrir una animación que rastrea el movimiento de los dedos y mueve una colección de UIButtons a lo largo de la trayectoria externa de un círculo

Me imagino que tendrá una especie de revólver. Al igual que cada uno se traba en su lugar en la parte inferior

o como deslizar a través de uno de los insertos de diapositivas de estos

gracias por adelantado


(Código de muestra en GitHub )

No es realmente tan difícil, hay mucha trigonometría involucrada.

Ahora, lo que voy a describir ahora, no es una animación , ya que solicitó que rastree la posición de su dedo en el título. Una animación implicaría su propia función de tiempo, pero como está utilizando el gesto táctil, podemos usar el tiempo inherente que tiene este evento y simplemente rotar la vista en consecuencia. (TL; DR: el usuario guarda el tiempo del movimiento, no un temporizador implícito).

Hacer un seguimiento del dedo

Antes que nada, definamos una clase conveniente que haga un seguimiento del ángulo, lo llamaré DialView . En realidad, es solo una subclase de UIView, que tiene la siguiente propiedad:

DialView.h

@interface DialView : UIView @property (nonatomic,assign) CGFloat angle; @end

DialView.m

- (void)setAngle:(CGFloat)angle { _angle = angle; self.transform = CGAffineTransformMakeRotation(angle); }

Los UIButton s pueden estar contenidos dentro de esta vista (no estoy seguro si quieres que los botones sean responsables de la rotación? Voy a usar un UIPanGestureRecognizer , ya que es la forma más conveniente).

Construyamos el controlador de vista que manejará un gesto de panoramización dentro de DialView , también mantengamos una referencia a 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

Depende de usted cómo se engancha el gesto de panorámica, personalmente, lo hice en el archivo de punta. Ahora, el cuerpo principal de esta función:

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 es un método inventado por mí, es simplemente un buen contenedor para atan2 (¡y estoy lanzando CGPointDistance si llamas AHORA!):

CGFloat CGPointAngle(CGPoint a, CGPoint b) { return atan2(a.y - b.y, a.x - b.x); } CGFloat CGPointDistance(CGPoint a, CGPoint b) { return sqrt(pow((a.x - b.x), 2) + pow((a.y - b.y), 2)); }

La clave aquí, es que hay dos ángulos para hacer un seguimiento:

  1. El ángulo de la vista en sí
  2. El ángulo formado entre su dedo y el centro de la vista.

En este caso, queremos que el ángulo de la vista sea originalAngle + deltaFinger , originalAngle + deltaFinger , y eso es lo que hace el código anterior, simplemente encapsulo todo el estado en una estructura.

Verificando el radio

Si desea realizar un seguimiento en "el borde de la vista", debe usar el método CGPointDistance y verificar si la distancia entre el begin.center y el now.finger es un valor específico.

¡Eso es tarea para ti!

Retrocediendo

Ok, ahora, esta parte es una animación real, ya que el usuario ya no tiene control sobre ella.

El ajuste se puede lograr teniendo un conjunto definido de ángulos, y cuando el dedo suelta el dedo (gesto finalizado), retroceda a ellos, de esta forma:

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; }]; }

La variable buttons , es solo un sustituto de la cantidad de botones que están en la vista. La ecuación es muy simple en realidad, solo abusa de la función de redondeo para aproximarse al ángulo de cierre.