iOS 转场动画封装

作者: Lucky闪闪星 | 来源:发表于2017-09-27 15:18 被阅读115次

    转场(transition)动画

    1.转场种类

    分为4种:present,dismiss; push, pop.
    前两者是presentViewController时的方式.
    后两者是在navigationController中的方式.

    2.针对不同种类的转场动画

    (1)对于前两者, 需要实现UIViewControllerTransitioningDelegate,
    并在viewController中设置该delegate.

    设置transitioningDelegate:

    override func viewDidLoad() {
            super.viewDidLoad()
            self.transitioningDelegate = self
    }
    

    实现UIViewControllerTransitioningDelegate:

    extension GreenViewController: UIViewControllerTransitioningDelegate {
        func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
            return ShapeAnimator(duration: TimeInterval(animationDurationSlider.value), viewCenter: self.view.center, viewSize: self.view.bounds.size, startPoint: self.transitionButton.center, transitionMode: .push)
        }
        func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
            return ShapeAnimator(duration: TimeInterval(animationDurationSlider.value), viewCenter: self.view.center, viewSize: self.view.bounds.size, startPoint: self.transitionButton.center, transitionMode: .push)
        }
    }
    

    forPresented 和 forDismissed 方法 分别对应present 和 dismiss.

    注意到方法有返回值,且返回值类型为UIViewControllerAnimatedTransitioning

    这实际上就是转场动画需要实现的协议,转场动画的关键也在于此.
    下文会着重讲解这个部分.

    (2)对于后两者,需要实现UINavigationControllerDelegate,并在ViewController中设置navigationController的delegate.

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        self.navigationController?.delegate = self
    }
    

    实现UINavigationControllerDelegate

    extension GreenViewController: UINavigationControllerDelegate {
        func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationControllerOperation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
            if operation == .push {
                if isAnimationSpring {
                    return SpringAnimator(duration: TimeInterval(animationDurationSlider.value), velocity: CGFloat(initialVelocitySlider.value), damping: CGFloat(springSlider.value))
                } else {
                    return ShapeAnimator(duration: TimeInterval(animationDurationSlider.value), viewCenter: fromVC.view.center, viewSize: fromVC.view.bounds.size, startPoint: self.transitionButton.center, transitionMode: .push)
                }
            } else {
                return nil
            }
        }
    }
    

    push以及pop的类型可以通过operation获取.

    同样针对不同的类型,也需要返回实现了UIViewControllerAnimatedTransitioning的对象.

    所以,核心就是封装一个实现了UIViewControllerAnimatedTransitioning的对象.

    而这个对象是可以在present/navigate动画中复用的.

    3.封装一个实现UIViewControllerAnimatedTransitioning的对象.

    直接上代码:
    关键就是实现UIViewControllerAnimatedTransitioning的协议,
    transitionContext是转场的上下文,
    可以通过它获取到动画容器视图,转场前的视图,转场后的视图.
    并在动画容器视图中实现动画效果即可.
    在transitionDuration()可以设置转场动画的时长.

    import UIKit
    
    class SpringAnimator: NSObject {
        
        var animationDuration: TimeInterval = 2
        var initialVelocity: CGFloat = 0
        var springDamping: CGFloat = 0.4
        
        convenience init(duration: TimeInterval, velocity: CGFloat, damping: CGFloat) {
            self.init()
            animationDuration = duration
            initialVelocity = velocity
            springDamping = damping
        }
    }
    
    extension SpringAnimator: UIViewControllerAnimatedTransitioning {
        func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
            return animationDuration
        }
        
        func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
            let toViewController = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to)
            let containerView = transitionContext.containerView
            
            let fromView = transitionContext.view(forKey: UITransitionContextViewKey.from)
            let toView = transitionContext.view(forKey: UITransitionContextViewKey.to)
            
            toView?.frame = fromView?.frame.offsetBy(dx: 0, dy: -(fromView?.frame.size.height ?? 0)) ?? .zero
            toView?.alpha = 0
            
            containerView.addSubview(toView!)
            
            let duration = self.transitionDuration(using: transitionContext)
            
            UIView.animate(withDuration: duration, delay: 0, usingSpringWithDamping: springDamping, initialSpringVelocity: initialVelocity, options: .curveLinear, animations: {
                toView!.alpha = 1.0
                toView?.frame = transitionContext.finalFrame(for: toViewController!)
            }) { (isFinished) in
                let wasCancelled = transitionContext.transitionWasCancelled
                transitionContext.completeTransition(!wasCancelled)
            }
        }
    }
    

    至此,一个转场动画效果就封装好啦,demo可以去我的github下载:https://github.com/LoKiLyn/TransitionAnimation

    相关文章

      网友评论

        本文标题:iOS 转场动画封装

        本文链接:https://www.haomeiwen.com/subject/cblgsxtx.html