I wrote a custom swipe transition that works fine on a modal presentation. But in a push presentation the "to" view position is not animating.
I've tried the same code with switching the translation with alpha and it works.
The from view works perfectly, it's just the to view that stays fixed during the animation.
func transitionAnimator(using transitionContext: UIViewControllerContextTransitioning) -> UIViewImplicitlyAnimating {
let duration = transitionDuration(using: transitionContext)
let container = transitionContext.containerView
let toController = transitionContext.viewController(forKey: .to)
toController?.beginAppearanceTransition(true, animated: true)
guard
let to = transitionContext.view(forKey: .to),
let from = transitionContext.view(forKey: .from)
else {
print("To or from view are nil!")
fatalError()
}
container.addSubview(to)
let animator = UIViewPropertyAnimator(duration: duration, curve: .linear)
var toStartingPoint: CGPoint
var fromEndingPoint: CGPoint
switch self.from {
case .down:
toStartingPoint = CGPoint(x: 0, y: -from.bounds.height)
fromEndingPoint = CGPoint(x: 0, y: from.bounds.height)
case .top:
toStartingPoint = CGPoint(x: 0, y: from.bounds.height)
fromEndingPoint = CGPoint(x: 0, y: -from.bounds.height)
case .right:
toStartingPoint = CGPoint(x: from.bounds.width, y: 0)
fromEndingPoint = CGPoint(x: -from.bounds.width, y: 0)
case .left:
toStartingPoint = CGPoint(x: -from.bounds.width, y: 0)
fromEndingPoint = CGPoint(x: from.bounds.width, y: 0)
}
to.transform = CGAffineTransform(translationX: toStartingPoint.x, y: toStartingPoint.y)
animator.addAnimations({
from.transform = CGAffineTransform(translationX: fromEndingPoint.x, y: fromEndingPoint.y)
}, delayFactor: 0.0)
animator.addAnimations({
to.transform = .identity
}, delayFactor: 0.0)
animator.addCompletion { [weak self] position in
switch position {
case .start:
self?.auxCancelCompletion?()
transitionContext.completeTransition(false)
self?.auxAnimationsCancel?()
case .end:
self?.auxEndCompletion?()
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
from.transform = .identity
to.transform = .identity
default:
transitionContext.completeTransition(false)
self?.auxAnimationsCancel?()
}
}
if let auxAnimations = auxAnimations {
animator.addAnimations(auxAnimations)
}
self.animator = animator
self.context = transitionContext
animator.addCompletion { [unowned self] _ in
self.animator = nil
self.context = nil
}
animator.isUserInteractionEnabled = true
return animator
}
I was thinking that was a problem about delegates but the navigationDelgate is correctly set, otherwise I think I wouldn't see any animation..
Delegate setting:
override func viewDidLoad() {
super.viewDidLoad()
transitionHelper = SwipeInteractiveTransitionHelper(withDelegate: self)
}
extension TodayViewController: UINavigationControllerDelegate {
func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationController.Operation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return transitionHelper?.swipeTransition
}
func navigationController(_ navigationController: UINavigationController, interactionControllerFor animationController: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
return transitionHelper?.swipeTransition
}
}
and here is the custom push coordinator, where the viewController is the next view controller, and where I attach the delegate.
case .pushCustom:
guard let navigationController = currentViewController.navigationController else {
fatalError("Can't push a view controller without a current navigation controller")
}
guard let current = currentViewController as? UINavigationControllerDelegate else {
fatalError("Can't push a view controller without a current navigation delegate")
}
navigationController.delegate = current
navigationController.pushViewController(viewController, animated: true) { [weak self] in
self?.currentViewController = SceneCoordinator.actualViewController(for: viewController)
completion?()
}
Solved animating a snapshot of the destination view, instead of directly animating the destination view.
let to = transitionContext.view(forKey: .to) let toViewSnapshot = to.snapshotView(afterScreenUpdates: true)
Just use the toViewSnapshot for the animation