Étant donné que c'est le meilleur résultat sur Google, j'ai pensé partager ce que je pense être la manière la plus sensée; qui consiste à utiliser l'API de transition iOS 7+. J'ai implémenté cela pour iOS 10 avec Swift 3.
Il est assez simple de combiner cela avec la façon dont UINavigationController
s'anime entre deux contrôleurs de vue si vous créez une sous-classe de UINavigationController
et renvoyez une instance d'une classe conforme au UIViewControllerAnimatedTransitioning
protocole.
Par exemple, voici ma UINavigationController
sous - classe:
class NavigationController: UINavigationController {
init() {
super.init(nibName: nil, bundle: nil)
delegate = self
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
extension NavigationController: UINavigationControllerDelegate {
public func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationControllerOperation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return NavigationControllerAnimation(operation: operation)
}
}
Vous pouvez voir que je mets le UINavigationControllerDelegate
à lui-même, et dans une extension de ma sous-classe, j'implémente la méthode UINavigationControllerDelegate
qui vous permet de retourner un contrôleur d'animation personnalisé (c'est-à-dire NavigationControllerAnimation
). Ce contrôleur d'animation personnalisé remplacera l'animation stock pour vous.
Vous vous demandez probablement pourquoi je passe l'opération à l' NavigationControllerAnimation
instance via son initialiseur. Je le fais pour que dans NavigationControllerAnimation
la mise en œuvre du UIViewControllerAnimatedTransitioning
protocole, je sache quelle est l'opération (c'est-à-dire «pousser» ou «pop»). Cela permet de savoir quel type d'animation je dois faire. La plupart du temps, vous souhaitez effectuer une animation différente en fonction de l'opération.
Le reste est assez standard. Implémentez les deux fonctions requises dans le UIViewControllerAnimatedTransitioning
protocole et animez comme vous le souhaitez:
class NavigationControllerAnimation: NSObject, UIViewControllerAnimatedTransitioning {
let operation: UINavigationControllerOperation
init(operation: UINavigationControllerOperation) {
self.operation = operation
super.init()
}
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return 0.3
}
public func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
guard let fromViewController = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from),
let toViewController = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to) else { return }
let containerView = transitionContext.containerView
if operation == .push {
// do your animation for push
} else if operation == .pop {
// do your animation for pop
}
}
}
Il est important de se rappeler que pour chaque type d'opération différent (c'est-à-dire «pousser» ou «pop»), les contrôleurs de vue vers et depuis seront différents. Lorsque vous êtes en mode push, le contrôleur to view sera celui qui est poussé. Lorsque vous êtes dans une opération de pop, le contrôleur de vue sera celui vers lequel la transition est effectuée et le contrôleur de vue sera celui qui sera sauté.
En outre, le to
contrôleur de vue doit être ajouté en tant que sous-vue de containerView
dans le contexte de transition.
Une fois votre animation terminée, vous devez appeler transitionContext.completeTransition(true)
. Si vous effectuez une transition interactive, vous devrez retourner dynamiquement un Bool
à completeTransition(didComplete: Bool)
, selon que la transition est terminée à la fin de l'animation.
Enfin ( lecture facultative ), vous voudrez peut-être voir comment j'ai effectué la transition sur laquelle je travaillais. Ce code est un peu plus hacky et je l'ai écrit assez rapidement donc je ne dirais pas que c'est un excellent code d'animation mais il montre toujours comment faire la partie animation.
La mienne était une transition vraiment simple; Je voulais imiter la même animation que UINavigationController, mais au lieu de l'animation `` page suivante en haut '', je voulais implémenter une animation 1: 1 de l'ancien contrôleur de vue en même temps que la nouvelle vue contrôleur apparaît. Cela a pour effet de donner l'impression que les deux contrôleurs de vue sont attachés l'un à l'autre.
Pour l'opération de poussée, cela nécessite d'abord de définir l' toViewController
origine de la vue du sur l'écran hors axe x, de l'ajouter comme sous-vue du containerView
, de l'animer sur l'écran en la mettant origin.x
à zéro. En même temps, j'anime la fromViewController
vue en la origin.x
désactivant de l'écran:
toViewController.view.frame = containerView.bounds.offsetBy(dx: containerView.frame.size.width, dy: 0.0)
containerView.addSubview(toViewController.view)
UIView.animate(withDuration: transitionDuration(using: transitionContext),
delay: 0,
options: [ UIViewAnimationOptions.curveEaseOut ],
animations: {
toViewController.view.frame = containerView.bounds
fromViewController.view.frame = containerView.bounds.offsetBy(dx: -containerView.frame.size.width, dy: 0)
},
completion: { (finished) in
transitionContext.completeTransition(true)
})
L'opération pop est essentiellement l'inverse. Ajoutez le en toViewController
tant que sous-vue du containerView
, et animez le fromViewController
vers la droite pendant que vous l'animez toViewController
depuis la gauche:
containerView.addSubview(toViewController.view)
UIView.animate(withDuration: transitionDuration(using: transitionContext),
delay: 0,
options: [ UIViewAnimationOptions.curveEaseOut ],
animations: {
fromViewController.view.frame = containerView.bounds.offsetBy(dx: containerView.frame.width, dy: 0)
toViewController.view.frame = containerView.bounds
},
completion: { (finished) in
transitionContext.completeTransition(true)
})
Voici un aperçu de l'ensemble du fichier Swift:
https://gist.github.com/alanzeino/603293f9da5cd0b7f6b60dc20bc766be