20210716, found some online exit animation, run, a face meng

This article discusses, how to solve

Entrance animation, very simple

  • Specifies the animation time, through the func transitionDuration (using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval

  • How to animation, through the func animateTransition (using transitionContext: UIViewControllerContextTransitioning)

Left hand from View Controller, previous Controller,

One on the right, to View Controller, the next Controller,

Let the last one disappear, let the next one appear

Uiview. animate Adjusts the animation properties,

Basically, I’m going to change the view frame, and that’s it

The following code, the effect is relatively simple

You present a controller, from the bottom up,

You can do it here, up, down, left, right, you can push out the controller

enum PresentingDirection{ case top, right, left, bottom var bounds: CGRect{ UIScreen.main.bounds } func offsetF(withFrame viewFrame: CGRect) -> CGRect{ let h = bounds.size.height let w = bounds.size.width switch self { case .top: return viewFrame.offsetBy(dx: 0, dy: -h) case .bottom: return viewFrame.offsetBy(dx: 0, dy: h) case .left: return viewFrame.offsetBy(dx: -w, dy: 0) case .right: return viewFrame.offsetBy(dx: w, dy: 0) } } } class CustomPresentationController: NSObject, UIViewControllerAnimatedTransitioning{ fileprivate var presentingDirection: PresentingDirection init(direction orientation: PresentingDirection) { presentingDirection = orientation } func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { return 1 } func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { guard let fromCtrl = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from), let toCtrl = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to) else{ return } let finalCtrlFrame = transitionContext.finalFrame(for: toCtrl) let containerView = transitionContext.containerView toCtrl.view.frame = presentingDirection.offsetF(withFrame: finalCtrlFrame) containerView.addSubview(toCtrl.view) UIView.animate(withDuration: transitionDuration(using: TransitionContext), Delay: 0.0, usingSpringWithDamping: 0.5, initialSpringVelocity: 0.0, options: .curvelinear) {fromctrl.view.alpha = 0.5 toCtrl.view.frame = finalCtrlFrame} completion: { _ in fromCtrl.view.alpha = 1 transitionContext.completeTransition(true) } } }Copy the code

Appearance animation, updated

Follow the example above

1. Make do

The familiar recipe, again, gets rid of both methods,

One is animation time, one is how animation

class CustomDismissController: NSObject, UIViewControllerAnimatedTransitioning{ fileprivate var presentingDirection: PresentingDirection init(direction orientation: PresentingDirection) { presentingDirection = orientation } func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { return 1 } func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { guard let fromCtrl = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from), let toCtrl = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to), let toView = toCtrl.view else{ return } let finalCtrlFrame = transitionContext.finalFrame(for: FromCtrl) let containerView = transitionContext. ContainerView fromCtrl. The alpha = 0.5 toView. Frame = presentingDirection.offsetF(withFrame: finalCtrlFrame) containerView.bringSubviewToFront(toView) UIView.animate(withDuration: transitionDuration(using: TransitionContext), Delay: 0.0, usingSpringWithDamping: 0.5, initialSpringVelocity: 0.0, options: .curveLinear) { toView.frame = finalCtrlFrame } completion: { _ in let success = ! transitionContext.transitionWasCancelled transitionContext.completeTransition(success) } } }Copy the code

Two key

  • FromCtrl. View. Alpha = 0.5

This one guarantees that when we leave the scene,

From. view doesn’t block toCtrl.view completely

  • containerView.bringSubviewToFront(toView)

This ensures that toCtrl.view will not be lost after the exit

Better solution idea

Animation is a snapshot thing,

I have a FROM controller on my left hand and a to controller on my right hand, and I’ll just lay it out

class CustomDismissController: NSObject, UIViewControllerAnimatedTransitioning{ fileprivate var presentingDirection: PresentingDirection init(direction orientation: PresentingDirection) { presentingDirection = orientation } func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { return 1 } func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { guard let fromCtrl = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from), let toCtrl = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to), let toView = toCtrl.view, let snapshot = toView.snapshotView(afterScreenUpdates: true) else{ return } let finalCtrlFrame = transitionContext.finalFrame(for: fromCtrl) let containerView = transitionContext.containerView snapshot.frame = presentingDirection.offsetF(withFrame: finalCtrlFrame) containerView.addSubview(snapshot) containerView.bringSubviewToFront(toView) toView.frame = finalCtrlFrame UIView.animate(withDuration: transitionDuration(using: transitionContext), delay: 0.0, usingSpringWithDamping: 0.5, initialSpringVelocity: 0.0, options: .curveLinear) { snapshot.frame = finalCtrlFrame } completion: { _ in snapshot.removeFromSuperview() let success = ! transitionContext.transitionWasCancelled transitionContext.completeTransition(success) } } }Copy the code

details

In the code above, if I tweak it,

Take the snapshot of the from controller, put it at the bottom,

Take the view of the TO controller, put it on top of the snapshot of the FROM controller, and animate it

Found, can not be placed up. Setting is invalid

Also, the view of the to controller, removed from the superview, can’t be retrieved

Another way to think about it

The view of the FROM controller, which is above the view of the to controller,

You can change the background color of the FROM controller to transparent and hide everything

example

Here’s the exit animation, which is a little more complicated,

Use keyframe animation, add a few steps

class FlipDismissAnimationController: NSObject, UIViewControllerAnimatedTransitioning { private let destinationFrame: CGRect let interactionController: SwipeInteractionController? init(destinationFrame: CGRect, interactionController: SwipeInteractionController?) { self.destinationFrame = destinationFrame self.interactionController = interactionController } func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { return 3 } func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { guard let fromVC = transitionContext.viewController(forKey: .from), let toVC = transitionContext.viewController(forKey: .to), let toView = toVC.view, let snapshot = fromVC.view.snapshotView(afterScreenUpdates: false) else { return } snapshot.layer.cornerRadius = CardViewController.cardCornerRadius snapshot.layer.masksToBounds = true let containerView = transitionContext.containerView containerView.addSubview(snapshot) if let fromC = fromVC as? RevealViewController{ fromC.imageView.isHidden = true fromC.view.backgroundColor = UIColor.clear } AnimationHelper.perspectiveTransform(for: containerView) toView.layer.transform = AnimationHelper.yRotation(-.pi / 2) let duration = transitionDuration(using: transitionContext) containerView.bringSubviewToFront(toView) UIView.animateKeyframes( withDuration: duration, delay: 0, options:. CalculationModeCubic, animations: {uiView. addKeyframe(withRelativeStartTime: 0.0, relativeDuration: 1/3) { snapshot.frame = self.destinationFrame } UIView.addKeyframe(withRelativeStartTime: 1/3, relativeDuration: 1/3) { snapshot.layer.transform = AnimationHelper.yRotation(.pi / 2) } UIView.addKeyframe(withRelativeStartTime: Two-thirds, relativeDuration: 1/3) {toView. Layer. The transform = AnimationHelper. YRotation (0.0)}}, completion: { _ in snapshot.removeFromSuperview() if transitionContext.transitionWasCancelled { containerView.sendSubviewToBack(toView) } transitionContext.completeTransition(! transitionContext.transitionWasCancelled) }) } }Copy the code

Finally, fill in the foundation

Transition animation structure drawing:

  • Every controller has a property,transitioningDelegate

By the method of UIViewControllerTransitioningDelegate agreement to provide the corresponding special animations

  • Transition contextTransitioning Context

Context is everything

You can get the left hand FROM controller, and the right hand to controller from inside,

Frame corresponding to the controller

You can also take the from and to views directly from the context,

Personally, not much

  • The animation is done,

Calls the method completeTransition(_:), which gives feedback to the system framework on the completion of the animation

github repo