Dato che questo è il miglior risultato su Google, ho pensato di condividere quello che penso sia il modo più sano; che consiste nell'utilizzare l'API di transizione iOS 7+. Ho implementato questo per iOS 10 con Swift 3.
È abbastanza semplice combinare questo con il modo in cui si UINavigationControlleranima tra due controller di visualizzazione se si crea una sottoclasse di UINavigationControllere si restituisce un'istanza di una classe conforme al UIViewControllerAnimatedTransitioningprotocollo.
Ad esempio, ecco la mia UINavigationControllersottoclasse:
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)
}
}
Puoi vedere che ho impostato il valore UINavigationControllerDelegatesu se stesso e in un'estensione nella mia sottoclasse implemento il metodo UINavigationControllerDelegateche ti consente di restituire un controller di animazione personalizzato (ovvero, NavigationControllerAnimation). Questo controller di animazione personalizzato sostituirà l'animazione stock per te.
Probabilmente ti starai chiedendo perché passo l'operazione NavigationControllerAnimationall'istanza tramite il suo inizializzatore. Lo faccio in modo tale che NavigationControllerAnimation, nell'implementazione del UIViewControllerAnimatedTransitioningprotocollo, sappia quale sia l'operazione (ovvero, "push" o "pop"). Questo aiuta a sapere che tipo di animazione dovrei fare. Il più delle volte, si desidera eseguire un'animazione diversa a seconda dell'operazione.
Il resto è piuttosto standard. Implementa le due funzioni richieste nel UIViewControllerAnimatedTransitioningprotocollo e anima come preferisci:
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
}
}
}
È importante ricordare che per ogni diverso tipo di operazione (ad esempio, "push" o "pop"), i controller da e verso la vista saranno diversi. Quando si esegue un'operazione push, il controller per visualizzare sarà quello che viene premuto. Quando ci si trova in un'operazione pop, il controller di visualizzazione sarà quello a cui si sta eseguendo la transizione e il controller di visualizzazione sarà quello che viene visualizzato.
Inoltre, il tocontroller di visualizzazione deve essere aggiunto come sottoview containerViewnel contesto di transizione.
Al termine dell'animazione, è necessario chiamare transitionContext.completeTransition(true). Se stai eseguendo una transizione interattiva, dovrai restituire dinamicamente un Boola completeTransition(didComplete: Bool), a seconda che la transizione sia completa alla fine dell'animazione.
Infine ( lettura facoltativa ), potresti voler vedere come ho fatto la transizione su cui stavo lavorando. Questo codice è un po 'più confuso e l'ho scritto abbastanza rapidamente, quindi non direi che è un ottimo codice di animazione ma mostra comunque come eseguire la parte di animazione.
La mia è stata una transizione davvero semplice; Volevo imitare la stessa animazione di UINavigationController in genere, ma invece dell'animazione "Pagina successiva sopra", volevo implementare un'animazione 1: 1 del vecchio controller di vista contemporaneamente alla nuova vista appare il controller. Ciò ha l'effetto di far sembrare i due controller di vista come se fossero bloccati l'uno all'altro.
Per l'operazione push, ciò richiede prima di impostare l' toViewControllerorigine della vista sullo schermo dell'asse x, aggiungendola come sottoview della containerView, animandola sullo schermo impostandola origin.xsu zero. Allo stesso tempo, anima la fromViewControllervista di distanza impostandola origin.xfuori dallo schermo:
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'operazione pop è sostanzialmente l'inverso. Aggiungi toViewControllercome sottoview di containerView, e anima via fromViewControllera destra mentre anima in toViewControllerda sinistra:
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)
})
Ecco un riassunto con l'intero file rapido:
https://gist.github.com/alanzeino/603293f9da5cd0b7f6b60dc20bc766be