Rotación de UIView con CGAffineTransform: ¿cómo hacer que vaya a la ruta completa y no a la corta?

Estoy haciendo una aplicación de velocímetro y quiero que la flecha se comporte de la manera correcta: si lanzo desde cero a 200 km en un segundo, quiero que pase alnetworkingedor de la barra, pero si pongo mi ángulo de rotation lo suficientemente grande, va el path corto, desde el background.
¿Cómo lo hago casi en círculo y no a través de un path corto?

Aquí está el código (trivial) que uso para la rotation:

[UIView animateWithDuration:0.3 animations:^(){ self.arrow.transform = CGAffineTransformRotate(CGAffineTransformIdentity, -4.4); }]; 

Me imagino que puedo rotarlo en pequeños fragments, pero a veces puede ser necesario rotarlo rápidamente de cero a máximo (por ejemplo, si no tuviéramos una lectura de velocidad y lo obtuviéramos a alta velocidad, entonces necesitamos rotar la flecha en su mayor parte la pantalla).

Como una pregunta secundaria: ¿cómo coloco animaciones para que las pueda aplicar una por una?

Tuve un problema similar hace un time. Solo necesitaba ir 180 grados, pero a veces lo necesitaba en el sentido de las agujas del reloj, a veces en el sentido contrario a las agujas del reloj, pero siempre giraba hacia atrás y adelante entre los dos, de ahí la propiedad "rotada" en el código.

Debe usar CALayer y CAKeyframeAnimations, y aquí está el código que funcionó para mí:

 -(void) showHideSpinnerWithDuration:(float) duration; { CALayer* layer = _spinner.layer; CAKeyframeAnimation* animation; animation = [CAKeyframeAnimation animationWithKeyPath:@"transform.rotation.z"]; animation.duration = duration; animation.cumulative = YES; animation.repeatCount = 1; animation.removedOnCompletion = NO; animation.fillMode = kCAFillModeForwards; if (_rotated) { animation.values = [NSArray arrayWithObjects: [NSNumber numberWithFloat:DegreesToRadians(180)], [NSNumber numberWithFloat:DegreesToRadians(0)], nil]; self.rotated = NO; } else { animation.values = [NSArray arrayWithObjects: [NSNumber numberWithFloat:DegreesToRadians(0)], [NSNumber numberWithFloat:DegreesToRadians(180)], nil]; self.rotated = YES; } animation.keyTimes = [NSArray arrayWithObjects: [NSNumber numberWithFloat:0.0], [NSNumber numberWithFloat:duration], nil]; animation.timingFunctions = [NSArray arrayWithObjects: [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut], [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut], nil]; [layer addAnimation:animation forKey:@"transform.rotation.z"]; } 

Hice una function auxiliar para esto hace un time, que se incluye a continuación. Básicamente, UIKit es inútil para esto, tendrá que importar el marco QuartzCore y usar CAKeyframeAnimation como lo señaló @Paul Cezanne, o CABasicAnimation , que creo que manejará muy bien.

El uso es bastante simple, simplemente llame al método como se detalla a continuación, pasando la vista de la image que desea rotar, la rotation que desee en radianes y la duración de la animation y la vista de la image se animarán correctamente. Siempre tomará el "largo path" porque en realidad hay una dirección especificada aquí.

Si pasa valores positivos a la animation, girará en el sentido de las agujas del reloj y, naturalmente, los valores negativos provocarán la rotation en el sentido contrario a las agujas del reloj.

 [self rotateView:imageView rotationInRadians:M_PI duration:2.0]; - (void)rotateView:(UIImageView *)imageView rotationInRadians:(CGFloat)radians duration:(CFTimeInterval)duration { CABasicAnimation *rotationAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"]; [rotationAnimation setToValue:[NSNumber numberWithFloat:radians]]; [rotationAnimation setDuration:duration]; [rotationAnimation setCumulative:YES]; [rotationAnimation setFillMode:kCAFillModeForwards]; [rotationAnimation setRemovedOnCompletion:NO]; [imageView.layer addAnimation:rotationAnimation forKey:@"gaugeRotationAnimation"]; } 

SWIFT 3

Tenía exactamente el mismo problema, aquí está mi solución en caso de que ayude a alguien más.

Referencia a la capa que contiene el puntero:

 @IBOutlet weak var pointerImageView: UIImageView! 

Defina una variable para realizar un seguimiento de la última position del puntero:

 private var meterPointerRadians: CGFloat = 0 

Cree una function que establezca el valor del medidor usando un porcentaje. Ej .: velocidad del 50%

 func setMeter(dialPercentage: Double) { //define the start and end angles of your speed meter: Eg: from 90 to 180 (semi circle) let meterLowerRangeAngle: Double = 90.0 let meterUpperRangeAngle: Double = 180.0 //calculate the angle of the pointer in radians let range = meterUpperRangeAngle - meterLowerRangeAngle let angle = (dialPercentage * range / 100) + meterLowerRangeAngle let radians: CGFloat = CGFloat(angle * .pi / 180) rotatePointer(imageView: self.pointerImageView, rotationInRadians: radians, duration: 1) } 

Gire el puntero, en sentido horario o antihorario, según la última position del puntero.

 func rotatePointer(imageView: UIImageView, rotationInRadians: CGFloat, duration: CFTimeInterval) { let rotationAnimation = CABasicAnimation(keyPath: "transform.rotation.z") rotationAnimation.fromValue = meterPointerRadians rotationAnimation.toValue = rotationInRadians rotationAnimation.duration = duration rotationAnimation.isCumulative = true rotationAnimation.fillMode = kCAFillModeBoth rotationAnimation.isRemovedOnCompletion = false imageView.layer.add(rotationAnimation, forKey: "pointerRotationAnimation") meterPointerRadians = CGFloat(rotationInRadians) }