2012-03-11 12 views
8

Esta pregunta se ha hecho antes, pero de una manera ligeramente diferente y no he podido obtener ninguna de las respuestas para que funcione como yo quería, así que estoy esperando alguien con grandes habilidades de Core Animation puede ayudarme.Cómo voltear una UIView alrededor del eje x mientras se cambian simultáneamente las subvistas

Tengo un juego de cartas en una mesa. A medida que el usuario desliza hacia arriba o hacia abajo, el conjunto de tarjetas sube y baja por la mesa. Hay 4 cartas visibles en la pantalla en un momento dado, pero solo la segunda tarjeta muestra su cara. A medida que el usuario desliza la segunda tarjeta se voltea sobre su cara y la siguiente carta (dependiendo de la dirección del deslizamiento) cae en su lugar mostrando su cara.

he levantado mi clase de vista de la tarjeta como esto:

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

Y he tratado de voltear la tarjeta utilizando este pedazo 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 la siguiente problemas que no puedo entender:

  1. Solo la mitad de la tarjeta en el eje x está animada.
  2. Una vez volteado, la cara de la tarjeta está al revés y se refleja.
  3. Una vez que he volteado la tarjeta, no puedo hacer que la animación vuelva a ejecutarse. En otras palabras, puedo ejecutar el bloque de animación tantas veces como quiera, pero solo la primera vez se animará. Las veces siguientes que trato de animar conducen a un desvanecimiento entre las subvistas.

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

Si alguien se ha encontrado con estos problemas, sería genial ver sus soluciones. Aún mejor sería agregar una transformación de perspectiva a la animación para darle ese poco de realismo adicional.

Respuesta

11

Esto resultó ser manera más sencilla de lo que pensaba y no tener que utilizar ninguna biblioteca CoreAnimation para lograr el efecto. Gracias a @Aaron Hayman por la pista. Solía ​​transitionWithView:duration:options:animations:completion

Mi aplicación en el interior del contenedor de vista:

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

El truco era la opción UIViewAnimationOptionTransitionFlipFromBottom. Por cierto, Apple tiene este bit exacto de código en su documentación. También puede agregar otras animaciones al bloque, como redimensionar y mover.

2

Bien, esta no será una solución completa, pero señalaré algunas cosas que pueden ser útiles. No soy un gurú de Core-Animation, pero hice algunas rotaciones 3D en mi programa.

En primer lugar, no hay un 'respaldo' para una vista. Entonces, si giras algo por M_PI (180 grados), vas a mirar esa vista desde la parte posterior (por eso está boca abajo/reflejada).

no estoy seguro de lo que entendemos por:

Sólo la mitad de la tarjeta a través del eje x está animado.

Pero, podría ser útil considerar su punto de anclaje (el punto en el que se produce la rotación). Por lo general, está en el centro, pero a menudo necesita que sea de otra manera. Tenga en cuenta 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 menos que lo necesite). He aquí cómo se accede al punto de anclaje:

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

La razón por la animación sólo alguna vez se ejecuta una vez se debe a que las transformaciones son absolutos, no acumulativo. Considere que siempre está comenzando con la transformación de identidad y luego modificando eso, y tendrá sentido ...pero, básicamente, no se produce animación porque no hay nada para animar la segunda vez (la vista ya está en el estado en el que está solicitando que esté).

Si está animando de una vista a otra (y no puede usar [UIView transitionWithView:duration:options:animations:completion:];) tendrá que usar una animación de dos etapas. En la primera etapa de la animación, para la 'carta' que se está volteando hacia atrás, rotará la vista para desaparecer 'arriba/abajo/lo que sea' a M_PI_2 (en ese punto 'desaparecerá', o no visible, debido a su rotación). Y en la segunda etapa, se rota la parte trasera de la vista para desaparecer a 0 (que debería ser la transformación de identidad ... es decir, el estado normal de la vista). Además, tendrás que hacer exactamente lo contrario para la 'carta' que está apareciendo (en el lado frontal). Puede hacerlo implementando otro [UIView animateWithDuration:...] en el bloque de finalización del primero. Aunque te lo advierto, hacer esto puede ser un poco complicado. Especialmente porque quieres que las vistas tengan una "parte trasera", lo que básicamente requerirá animar 4 vistas (la vista para desaparecer, la vista para aparecer, la parte trasera de la vista para desaparecer y la parte trasera). de-ver-para-aparecer). Finalmente, en el bloque de finalización de la segunda animación puede hacer algo de limpieza (restablecer la vista que se gira y hacer su alfa 0.0f, etc ...).

Sé que esto es complicado, por lo que es posible que desee leer algún tutorial sobre Core-Animation.

+0

Gracias Aaron. El método transitionWithView me ha dado una pista para la solución. Dicho esto, me has dado una mejor comprensión de cómo las animaciones 3D. Es hora de que invierta en un buen libro Core Animation. –

1

@ Aaron tiene algo de buena información que deberías leer.

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

Por ejemplo, para crear una "tarjeta" que tiene una parte delantera y trasera se 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 se puede aplicar a su transformación cardContainer y tener una tarjeta de volteo.

+0

Si bien este método funcionará.Necesito tener UIViews con elementos interactivos en las tarjetas ya que el usuario necesita interactuar con las caras de la tarjeta. Utilizar CALayer's solo me permite trabajar con las imágenes, etc. –

+0

Coloque esta jerarquía en un 'UIView' que contenga las interacciones ... –

+0

Aha. Célebre. Parece que debería funcionar. Gracias. –

0

@ Paul.s

que siguieron su acercamiento con el recipiente de tarjetas pero cuando Applt la animación de rotación en el envase tarjeta sólo la mitad de la primera gira de la tarjeta alrededor de sí mismo y, finalmente, la vista todo el tiempo appears.Each un lado no se encuentra en la animación

0

sobre la base de esta Paul.s se actualiza para Swift 3 y le dará la vuelta 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) 
} 

animación de un total de 360, pero ya que las capas tienen diferentes zPositions los diferentes 'lados' de las capas mostrará

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") 
    } 
Cuestiones relacionadas