Transiciones y rotation personalizadas de iOS

Estoy usando transiciones personalizadas para mostrar una vista de juego modal a pantalla completa. Cuando el usuario inicia un juego, el controller de la vista raíz escala "en" la pantalla, mientras que el controller de vista de pantalla completa hace zoom de una escala más grande hacia abajo en la pantalla mientras se pasa de 0% de opacidad a 100% de opacidad. ¡Sencillo!

La transición se ve muy bien y funciona bien, y también invierte correctamente la animation al descartar el controller de la vista del juego.

El problema que estoy teniendo es que si el dispositivo se gira mientras se muestra el controller de vista de pantalla completa , al volver al controller de vista raíz, el layout es totalmente atornillado. Y la rotation adicional no soluciona el problema, el layout es atornillado y permanece maldito.

Si desactivo el uso de una transición personalizada, este problema desaparece. Además, si conservo la transición personalizada, pero inhabilita las llamadas configurando un CATransform3D en mis vistas de origen y destino en la animation de transición, el problema desaparece nuevamente.

Aquí está mi delegado en transición:

class FullscreenModalTransitionManager: NSObject, UIViewControllerAnimatedTransitioning, UIViewControllerTransitioningDelegate { private var presenting:Bool = true // MARK: UIViewControllerAnimatedTransitioning protocol methods func animateTransition(transitionContext: UIViewControllerContextTransitioning) { // get reference to our fromView, toView and the container view that we should perform the transition in let container = transitionContext.containerView() let fromView = transitionContext.viewForKey(UITransitionContextFromViewKey)! let toView = transitionContext.viewForKey(UITransitionContextToViewKey)! let scale:CGFloat = 1.075 //let bigScale = CGAffineTransformMakeScale(scale, scale) //let smallScale = CGAffineTransformMakeScale(1/scale,1/scale) let bigScale = CATransform3DMakeScale(scale, scale, 1) let smallScale = CATransform3DMakeScale(1/scale, 1/scale, 1) let smallOpacity:CGFloat = 0.5 let presenting = self.presenting if presenting { // when presenting, incoming view must be on top container.addSubview(fromView) container.addSubview(toView) toView.layer.transform = bigScale toView.opaque = false toView.alpha = 0 fromView.layer.transform = CATransform3DIdentity fromView.opaque = false fromView.alpha = 1 } else { // when !presenting, outgoing view must be on top container.addSubview(toView) container.addSubview(fromView) toView.layer.transform = smallScale toView.opaque = false toView.alpha = smallOpacity fromView.layer.transform = CATransform3DIdentity fromView.opaque = false fromView.alpha = 1 } let duration = self.transitionDuration(transitionContext) UIView.animateWithDuration(duration, delay: 0.0, usingSpringWithDamping: 0.7, initialSpringVelocity: 0, options: nil, animations: { if presenting { fromView.layer.transform = smallScale fromView.alpha = smallOpacity } else { fromView.layer.transform = bigScale fromView.alpha = 0 } }, completion: nil ) UIView.animateWithDuration(duration, delay: duration/6, usingSpringWithDamping: 0.7, initialSpringVelocity: 0.5, options: nil, animations: { toView.layer.transform = CATransform3DIdentity toView.alpha = 1 }, completion: { finished in transitionContext.completeTransition(true) }) } func transitionDuration(transitionContext: UIViewControllerContextTransitioning) -> NSTimeInterval { return 0.5 } // MARK: UIViewControllerTransitioningDelegate protocol methods func animationControllerForPresentedController(presented: UIViewController, presentingController presenting: UIViewController, sourceController source: UIViewController) -> UIViewControllerAnimatedTransitioning? { self.presenting = true return self } func animationControllerForDismissedController(dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? { self.presenting = false return self } } 

Y, para reference visual, aquí es cómo se ve mi controller de vista raíz:

Diseño correcto para mi controlador de vista raíz

Y, para reference visual, esto es lo que se ve mi controller de vista raíz si giro mi dispositivo (al less una vez) en la vista de juego y return al controller de vista raíz.

Disposición rota, después de rotar el dispositivo mientras está en la vista de pantalla completa y volver a la vista raíz

Y una nota final, en caso de que suene alguna campana, estoy usando classs de autolayout y tamaño para diseñar mi controller de vista raíz.

Gracias,

Tuve un problema similar, había escrito una transición personalizada para push and pop en un controller de navigation. Si giró el dispositivo después de hacer un push, cuando regresó al controller de vista raíz su marco no cambiaría.


Problema

introduzca la descripción de la imagen aquí


Solución

C objective

 - (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext { // ViewController Reference UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey]; // Fix layout bug in iOS 9+ toViewController.view.frame = [transitionContext finalFrameForViewController:toViewController]; // The rest of your code ... } 

Swift 3.0

 func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { // ViewController reference let toViewController = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to)! // Fix layout bug in iOS 9+ toViewController.view.frame = transitionContext.finalFrame(for: toViewController) // The rest of your code ... } 

Accidentalmente me di count de esto, mientras hacía un código de layout manual en las vistas sin transformaciones de identidad. Resulta que si su vista tiene una transformación no identificada, el código de layout normal falla.

La solución, en mi delegado de transición, era tomar la vista transitada, y en la animation, la callback completa establece su transformación en identidad (dado que esa vista es invisible al final de la animation y detrás de la nueva vista, esto no tiene efecto en la apariencia)

 UIView.animateWithDuration(duration, delay: 0.0, usingSpringWithDamping: 0.7, initialSpringVelocity: 0, options: nil, animations: { if presenting { fromView.transform = smallScale fromView.alpha = smallOpacity } else { fromView.transform = bigScale fromView.alpha = 0 } }, completion: { completed in // set transform of now hidden view to identity to prevent breakage during rotation fromView.transform = CGAffineTransformIdentity }) 

Finalmente encontré una solución para este problema. Necesita mejorar el código de @agilityvision. Debe agregar el valor BOOL, algo así como closeVCNow que indica que desea cerrar VC, y en animateTransition: en el bloque de animation, haga lo siguiente:

 if (self.closeVCNow) { toVC.view.transform = CGAffineTransformIdentity; toVC.view.frame = [transitionContext finalFrameForViewController:toVC]; self.closeVCNow = NO; }