Cómo voltear una UIView alnetworkingedor del eje x mientras simultáneamente cambia de vista

Esta pregunta se ha hecho anteriormente, pero de una manera ligeramente diferente y no pude conseguir que ninguna de las respuestas funcione de la manera que quería, así que espero que alguien con grandes habilidades de Core Animation pueda ayudarme.

Tengo un juego de cartas en una table. Cuando el usuario pulsa hacia arriba o hacia abajo, el set de cartas se mueve hacia arriba y hacia abajo de la table. Hay 4 tarjetas visibles en la pantalla en un momento dado, pero solo la segunda tarjeta muestra su cara. Cuando el usuario desliza la segunda tarjeta, se vuelve a colocar sobre su cara y la siguiente tarjeta (dependiendo de la dirección del slider) aterriza en su lugar mostrando su cara.

He configurado mi class de vista de tarjeta así:

@interface WLCard : UIView { UIView *_frontView; UIView *_backView; BOOL flipped; } 

Y he intentado voltear la tarjeta usando esta pieza de código:

 - (void) flipCard { [self.flipTimer invalidate]; if (flipped){ return; } id animationsBlock = ^{ self.backView.alpha = 1.0f; self.frontView.alpha = 0.0f; [self bringSubviewToFront:self.frontView]; flipped = YES; CALayer *layer = self.layer; CATransform3D rotationAndPerspectiveTransform = CATransform3DIdentity; rotationAndPerspectiveTransform.m34 = 1.0 / 500; rotationAndPerspectiveTransform = CATransform3DRotate(rotationAndPerspectiveTransform, M_PI, 1.0f, 0.0f, 0.0f); layer.transform = rotationAndPerspectiveTransform; }; [UIView animateWithDuration:0.25 delay:0.0 options: UIViewAnimationCurveEaseInOut animations:animationsBlock completion:nil]; } 

Este código funciona pero tiene los siguientes problemas con él que no puedo resolver:

  1. Solo se anima la mitad de la tarjeta en el eje x.
  2. Una vez girada, la cara de la tarjeta está invertida y reflejada.
  3. Una vez que he girado la tarjeta, no puedo volver a ejecutar la animation. En otras palabras, puedo ejecutar el bloque de animation tantas veces como quiera, pero solo la primera vez se animará. Las siguientes veces trato de animar llevar a un solo fundido entre las subvistas.

Además, tenga en count que necesito poder interactuar con la cara de la tarjeta. es decir, tiene botones en él.

Si alguien se ha topado con estos problemas, sería genial ver sus soluciones. Mejor sería agregar una transformación de perspectiva a la animation para darle ese toque extra de realismo.

Esto resultó ser mucho más simple de lo que pensaba y no tuve que usar ninguna biblioteca de CoreAnimation para lograr el efecto. Gracias a @Aaron Hayman por la pista. Usé transitionWithView:duration:options:animations:completion

Mi implementación dentro de la vista contenedor:

  [UIView transitionWithView:self duration:0.2 options:UIViewAnimationOptionTransitionFlipFromBottom animations: ^{ [self.backView removeFromSuperview]; [self addSubview:self.frontView]; } completion:NULL]; 

El truco fue la opción UIViewAnimationOptionTransitionFlipFromBottom . Incidentalmente, Apple tiene este bit exacto de código en su documentation. También puede agregar otras animaciones al bloque, como cambiar el tamaño y moverse.

Ok, esta no será una solución completa, pero señalaré algunas cosas que podrían ser útiles. No soy un gurú Core-Animation pero he hecho algunas rotaciones en 3D en mi progtwig.

Primero, no hay "vuelta" a una vista. Entonces, si giras algo por M_PI (180 grados) vas a mirar esa vista como si desde atrás (por lo que está invertida / reflejada).

No estoy seguro de a qué te refieres con:

Solo se anima la mitad de la tarjeta en el eje x.

Pero, podría ayudar a tener en count su punto de anclaje (el punto en el que se produce la rotation). Por lo general, está en el centro, pero a menudo lo necesita para ser de otra manera. Tenga en count que los puntos de anclaje se expresan como una proporción (porcentaje / 100) … por lo que los valores son 0 – 1.0f. Solo necesita configurarlo una vez (a less que lo necesite para cambiar). A continuación, le indicamos cómo accede al punto de anclaje:

layer.anchorPoint = CGPointMake(0.5f, 0.5f) //This is center

La razón por la que la animation solo se ejecuta una vez es porque las transformaciones son absolutas, no acumulativas. Tenga en count que siempre está comenzando con la transformación de identidad y luego modificando eso, y tendrá sentido … pero, básicamente, no se produce ninguna animation porque no hay nada para animar la segunda vez (la vista ya está en el estado en que está solicitando que esté en).

Si estás animando de una vista a otra (y no puedes usar [UIView transitionWithView:duration:options:animations:completion:]; ) tendrás que usar una animation de dos etapas. En la primera etapa de la animation, para la 'tarjeta' que se está volteando a la parte trasera, girará la vista para desaparecer 'arriba / abajo / lo que sea' a M_PI_2 (en ese punto será 'ido', o no visible, debido a su rotation). Y en la segunda etapa, gira el backside-of-view-to-disappear a 0 (que debería ser la transformación de identidad … también conocido como el estado normal de la vista). Además, tendrás que hacer exactamente lo contrario para la "tarjeta" que aparece (al lado del frente). Puede hacer esto implementando otro [UIView animateWithDuration:...] en el bloque de finalización del primero. Sin embargo, te lo advierto, hacer esto puede complicarse un poco. Especialmente porque quieres vistas para tener una 'parte trasera', lo que básicamente requerirá animar 4 vistas (la vista para desaparecer, la vista para aparecer, la parte trasera para ver y desaparecer, y la parte trasera, de-vista-a-aparecer). Finalmente, en el bloque de finalización de la segunda animation puedes hacer un poco de limpieza (restaurar la vista que se gira y hacer su alpha 0.0f, etc …).

Sé que esto es complicado, así que quizás quieras leer un tutorial sobre Core-Animation.

@Aaron tiene buena información que debes leer.

La solución más simple es usar un CATransformLayer que le permita colocar otros CALayer en su interior y mantener su jerarquía 3D.

Por ejemplo, para crear una "Tarjeta" que tenga un frente y una parte posterior, podría hacer algo como esto:

 CATransformLayer *cardContainer = [CATransformLayer layer]; cardContainer.frame = // some frame; CALayer *cardFront = [CALayer layer]; cardFront.frame = cardContainer.bounds; cardFront.zPosition = 2; // Higher than the zPosition of the back of the card cardFront.contents = (id)[UIImage imageNamed:@"cardFront"].CGImage; [cardContainer addSublayer:cardFront]; CALayer *cardBack = [CALayer layer]; cardBack.frame = cardContainer.bounds; cardBack.zPosition = 1; cardBack.contents = (id)[UIImage imageNamed:@"cardBack"].CGImage; // You may need to mirror this image [cardContainer addSublayer:cardBack]; 

Con esto, ahora puede aplicar su transformación a cardContainer y tener una tarjeta cardContainer .

@ Paul.s

Seguí su enfoque con el contenedor de tarjetas, pero cuando apliqué la animation de rotation en el contenedor de tarjetas, solo una mitad de la primera tarjeta gira alnetworkingedor de sí misma y finalmente aparece toda la vista. Cada vez que falta un lado en la animation

Basado en Paul.s esto se actualiza para Swift 3 y volteará una tarjeta en diagonal:

 func createLayers(){ transformationLayer = CATransformLayer(layer: CALayer()) transformationLayer.frame = CGRect(x: 15, y: 100, width: view.frame.width - 30, height: view.frame.width - 30) let black = CALayer() black.zPosition = 2 black.frame = transformationLayer.bounds black.backgroundColor = UIColor.black.cgColor transformationLayer.addSublayer(black) let blue = CALayer() blue.frame = transformationLayer.bounds blue.zPosition = 1 blue.backgroundColor = UIColor.blue.cgColor transformationLayer.addSublayer(blue) let tgr = UITapGestureRecognizer(target: self, action: #selector(recTap)) view.addGestureRecognizer(tgr) view.layer.addSublayer(transformationLayer) } 

Animar un 360 completo, pero dado que las capas tienen diferentes zPosiciones, los diferentes 'lados' de las capas mostrarán

 func recTap(){ let animation = CABasicAnimation(keyPath: "transform") animation.delegate = self animation.duration = 2.0 animation.fillMode = kCAFillModeForwards animation.isRemovedOnCompletion = false animation.toValue = NSValue(caTransform3D: CATransform3DMakeRotation(CGFloat(Float.pi), 1, -1, 0)) transformationLayer.add(animation, forKey: "arbitrarykey") }