// // DuckAnimationController.swift // Duckable // // Created by Shadowfacts on 11/7/22. // import UIKit @available(iOS 16.0, *) class DuckAnimationController: NSObject, UIViewControllerAnimatedTransitioning { let owner: DuckableContainerViewController let needsShrinkAnimation: Bool init(owner: DuckableContainerViewController, needsShrinkAnimation: Bool) { self.owner = owner self.needsShrinkAnimation = needsShrinkAnimation } func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { return 0.2 } func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { guard case .ducked(let duckable, placeholder: let placeholder) = owner.state, let presented = transitionContext.viewController(forKey: .from) else { transitionContext.completeTransition(false) return } guard transitionContext.isAnimated else { transitionContext.completeTransition(true) return } let container = transitionContext.containerView if needsShrinkAnimation { duckable.duckableViewControllerWillAnimateDuck(withDuration: 0.2, afterDelay: 0.2) let presentedFrameInContainer = container.convert(presented.view.bounds, from: presented.view) let heightToSlide = container.bounds.height - container.safeAreaInsets.bottom - detentHeight - presentedFrameInContainer.minY let slideAnimator = UIViewPropertyAnimator(duration: 0.4, dampingRatio: 1) slideAnimator.addAnimations { container.transform = CGAffineTransform(translationX: 0, y: heightToSlide + 10) } slideAnimator.addCompletion { _ in duckable.duckableViewControllerDidFinishAnimatingDuck() transitionContext.completeTransition(true) } slideAnimator.startAnimation() placeholder.view.transform = CGAffineTransform(translationX: 0, y: 10) let bounceAnimator = UIViewPropertyAnimator(duration: 0.1, curve: .linear) { placeholder.view.transform = .identity container.transform = CGAffineTransform(translationX: 0, y: heightToSlide) } bounceAnimator.startAnimation(afterDelay: 0.3) let fadeAnimator = UIViewPropertyAnimator(duration: 0.1, curve: .linear) { presented.view.layer.opacity = 0 } fadeAnimator.addCompletion { _ in presented.view.layer.opacity = 1 } fadeAnimator.startAnimation(afterDelay: 0.3) } else { duckable.duckableViewControllerWillAnimateDuck(withDuration: 0.2, afterDelay: 0) placeholder.view.transform = CGAffineTransform(translationX: 0, y: 10) let bounceAnimator = UIViewPropertyAnimator(duration: 0.1, curve: .linear) { placeholder.view.transform = .identity container.transform = CGAffineTransform(translationX: 0, y: -10) } bounceAnimator.startAnimation(afterDelay: 0.2) let fadeAnimator = UIViewPropertyAnimator(duration: 0.1, curve: .linear) { presented.view.layer.opacity = 0 } fadeAnimator.addCompletion { _ in presented.view.layer.opacity = 1 duckable.duckableViewControllerDidFinishAnimatingDuck() transitionContext.completeTransition(true) } fadeAnimator.startAnimation(afterDelay: 0.2) } } }