ios - ¿Cómo girar un objeto plano alrededor de su centro en una vista en perspectiva?
catransform3d (2)
Lo que estoy tratando de hacer parece que debería ser bastante fácil: creé una vista de arriba hacia abajo en 2D de un antiguo registro fonográfico. Quiero girarlo (colocarlo de nuevo) en su eje X y luego girarlo alrededor de su eje Z.
He leído todas las preguntas aquí que tienen CATransform3D en el cuerpo, leí el artículo "Matrices pueden ser tus amigos" de Steve Baker, así como el libro de Bill Dudney "Animación principal para Mac OS X y el iPhone" Creo que Brad Larson''s " La rotación 3D sin un trackball " tiene todo el código correcto, pero dado que le permite al usuario ajustar los tres ejes, me está costando mucho encoger su código en lo que percibo como una sola dimensión (un eje z girado ).
Aquí está la imagen con la que estoy probando, no que los detalles sean importantes para el problema:
Traigo eso en pantalla de la manera habitual: (en una subclase de UIView)
- (void)awakeFromNib
{
UIImage *recordImage = [UIImage imageNamed:@"3DgoldRecord"];
if (recordImage) {
recordLayer = [CALayer layer];
[recordLayer setFrame:CGRectMake(0.0, 0.0, 1024, 1024)];
[recordLayer setContents:(id)[[UIImage imageNamed:@"3DgoldRecord"] CGImage]];
[self.layer addSublayer:recordLayer];
}
}
Esa es la primera prueba, simplemente obteniéndola en la pantalla ;-)
Luego, para "recostarlo", aplico una transformación para rotar sobre el eje X de la capa, insertando este código después de establecer el contenido de la capa en la imagen y antes de agregar la subcapa:
CATransform3D myRotationTransform =
CATransform3DRotate(recordLayer.transform,
(M_PI_2 * 0.85), //experiment with flatness
1.0, // rotate only across the x-axis
0.0, // no y-axis transform
0.0); // no z-axis transform
recordLayer.transform = myRotationTransform;
Eso funcionó como se esperaba: el récord está descansando muy bien.
Y para el siguiente paso, haciendo girar el registro, vinculé esta animación al evento touchesEnded, aunque una vez fuera de la fase de prueba / aprendizaje, esta rotación no estará bajo el control del usuario:
CATransform3D currentTransform = recordLayer.transform; // to come back to at the end
CATransform3D myRotationTransform =
CATransform3DRotate(currentTransform,
1.0, // go all the way around once
(M_PI_2 * 0.85), // x-axis change
1.00, // y-axis change ?
0.0); // z-axis change ?
recordLayer.transform = myRotationTransform;
CABasicAnimation *myAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation"];
myAnimation.duration = 5.0;
myAnimation.fromValue = [NSNumber numberWithFloat:0.0];
myAnimation.toValue = [NSNumber numberWithFloat:M_PI * 2.0];
myAnimation.delegate = self;
[recordLayer addAnimation:myAnimation forKey:@"transform.rotation"];
Así que estoy bastante seguro de que estoy colgado en el vector en la llamada CATransform3DRotate (confía en mí: he estado intentando cambios simples en ese vector para ver el cambio ... lo que aparece aquí es simplemente el último cambio Lo intenté). Tal como lo entiendo, los valores para x, y, yz en la transformación son, en esencia, el porcentaje del valor pasado durante la animación que va desde desde Valor hasta Valor hasta.
Si estoy en el camino correcto para entender esto, ¿es posible expresar esto en una sola transformación? ¿O debo, para cada fotograma de animación efectivo, rotar la imagen vertical original ligeramente alrededor del eje zy luego colocar el resultado con una rotación del eje x? Vi una pregunta / respuesta que hablaba sobre la combinación de transformaciones, pero esa era una transformación de escala seguida de una transformación de rotación. Me he metido en la transformación de la transformación, pero no estoy haciendo lo que creo que debería obtener (es decir, pasar el resultado de una transformación al argumento de la transformación de la siguiente parece que solo se ejecuta uno por completo y luego se anima el otro).
Esto es más fácil si utiliza un CATransformLayer
como elemento principal de la capa de vista de la imagen. Por lo tanto, necesitará una subclase de vista personalizada que use CATransformLayer
como su capa. Esto es trivial:
@interface TransformView : UIView
@end
@implementation TransformView
+ (Class)layerClass {
return [CATransformLayer class];
}
@end
Coloque un TransformView
en su plumilla y agregue un UIImageView
como una subvista del TransformView
. Conecte estas vistas a puntos de venta en su controlador de vista llamado transformView
y discView
.
En su controlador de vista, configure la transformación de transformView
para aplicar la perspectiva (configurando m34
) y la inclinación del eje X:
- (void)viewDidLoad
{
[super viewDidLoad];
CATransform3D transform = CATransform3DIdentity;
transform.m34 = -1 / 500.0;
transform = CATransform3DRotate(transform, .85 * M_PI_2, 1, 0, 0);
self.transformView.layer.transform = transform;
}
Agregue una animación para la ruta clave transform.rotation.z
a discView
en viewWillAppear:
y elimínela en viewDidDisappear:
::
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
animation.fromValue = [NSNumber numberWithFloat:0];
animation.toValue = [NSNumber numberWithFloat:2 * M_PI];
animation.duration = 1.0;
animation.repeatCount = HUGE_VALF;
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
[self.discView.layer addAnimation:animation forKey:@"transform.rotation.z"];
}
- (void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated];
[self.discView.layer removeAllAnimations];
}
Resultado:
ACTUALIZAR
Aquí hay una demostración de Swift:
import UIKit
import XCPlayground
class TransformView: UIView {
override class func layerClass() -> AnyClass {
return CATransformLayer.self
}
}
let view = UIView(frame: CGRectMake(0, 0, 300, 150))
view.backgroundColor = UIColor.groupTableViewBackgroundColor()
XCPlaygroundPage.currentPage.liveView = view
let transformView = TransformView(frame: view.bounds)
view.addSubview(transformView)
var transform = CATransform3DIdentity
transform.m34 = CGFloat(-1) / transformView.bounds.width
transform = CATransform3DRotate(transform, 0.85 * CGFloat(M_PI_2), 1, 0, 0)
transformView.layer.transform = transform
let image = UIImage(named: "3DgoldRecord")!
let imageView = UIImageView(image: image)
imageView.bounds = CGRectMake(0, 0, 200, 200)
imageView.center = CGPointMake(transformView.bounds.midX, transformView.bounds.midY)
transformView.addSubview(imageView)
let animation = CABasicAnimation(keyPath: "transform.rotation.z")
animation.fromValue = 0
animation.toValue = 2 * M_PI
animation.duration = 1
animation.repeatCount = Float.infinity
animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
imageView.layer.addAnimation(animation, forKey: animation.keyPath)
Copie la imagen de la pregunta en la carpeta de recursos del patio de recreo y 3DgoldRecord.png
nombre 3DgoldRecord.png
. Resultado:
Puedes considerar envolver el recordLayer con un superlayer. Aplique la rotación del eje x a la súper capa y agregue la animación de rotación del eje z a recordLayer.