I have 2 nested view controllers with different animations. First one has alpha animation and second one moves from bottom to top with constraint.
My current implementation is just show/hide methods inside first view controller in which I run each animation manually with UIView.animate(withDuration:animations:)
. But it is not convenient for some reasons (for example I need to call custom methods instead of ones provided by iOS).
Is there a way somehow override present/dissmiss transition to allow to call default present/dismiss methods which will run my animations?
Found an appropriate answer here: https://habr.com/ru/articles/424853/
import UIKit
class AnimatorPresent: NSObject, UIViewControllerAnimatedTransitioning {
let startFrame: CGRect
init(startFrame: CGRect) {
self.startFrame = startFrame
}
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return 0.3
}
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
guard let vcTo = transitionContext.viewController(forKey: .to),
let snapshot = vcTo.view.snapshotView(afterScreenUpdates: true) else {
return
}
let vContainer = transitionContext.containerView
vcTo.view.isHidden = true
vContainer.addSubview(vcTo.view)
snapshot.frame = self.startFrame
vContainer.addSubview(snapshot)
UIView.animate(withDuration: 0.3, animations: {
snapshot.frame = (transitionContext.finalFrame(for: vcTo))
}, completion: { success in
vcTo.view.isHidden = false
snapshot.removeFromSuperview()
transitionContext.completeTransition(true)
})
}
}
import UIKit
class AnimatorDismiss: NSObject, UIViewControllerAnimatedTransitioning {
let endFrame: CGRect
init(endFrame: CGRect) {
self.endFrame = endFrame
}
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return 0.3
}
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
guard let vcTo = transitionContext.viewController(forKey: .to),
let vcFrom = transitionContext.viewController(forKey: .from),
let snapshot = vcFrom.view.snapshotView(afterScreenUpdates: true) else {
return
}
let vContainer = transitionContext.containerView
vContainer.addSubview(vcTo.view)
vContainer.addSubview(snapshot)
vcFrom.view.isHidden = true
UIView.animate(withDuration: 0.3, animations: {
snapshot.frame = self.endFrame
}, completion: { success in
transitionContext.completeTransition(true)
})
}
}
extension VCYellow: UIViewControllerTransitioningDelegate {
func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return AnimatorPresent(startFrame: self.startFrame)
}
func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return AnimatorDismiss(endFrame: self.startFrame)
}
}
In short we create classes implementing UIViewControllerAnimatedTransitioning
and place all the animation inside them. Then we add UIViewControllerTransitioningDelegate
somewhere and assign its object as transitioningDelegate
.