美文网首页
自定义控制器转场动画

自定义控制器转场动画

作者: 哈罗弟弟 | 来源:发表于2017-05-15 15:56 被阅读92次

前言

系统自带的push和pop动画已经能满足绝大部份使用场景了。腾讯新闻、汽车之家等App的push和pop动画和系统的不一样,是自定义的,找了点空闲时间,研究了哈自定义控制器转场动画。

QQ20170515-153720.gif

1、声明一个枚举,自定义一个指定构造器方法,根据枚举值来确定该使用push或者pop动画

    enum TransitionAniamtionsType {
    case push,pop
}
    var transitionAniamtionsType: TransitionAniamtionsType = .push
    
    public init(type: TransitionAniamtionsType) {
        transitionAniamtionsType = type
    }

2、新建一个类,遵守<UIViewControllerAnimatedTransitioning>协议,实现两个必需实现的方法

    //返回转场动画持续时间
    func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
        return 0.3
    }
    //转场动画的具体实现
    func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
        if transitionAniamtionsType == .push {
            pushAnimation(using: transitionContext)
        }else if transitionAniamtionsType == .pop {
            popAnimation(using: transitionContext)
        }
    }

3、具体动画逻辑,本人实现的动画比较简单,喜欢研究的大神可以根据需要实现更复杂的动画。具体动画实现代码如下

    //push
    fileprivate func pushAnimation(using transitionContext: UIViewControllerContextTransitioning) {
        //获取fromVc和toVc的view
        guard let fromView = transitionContext.view(forKey: UITransitionContextViewKey.from),
            let toView = transitionContext.view(forKey: UITransitionContextViewKey.to) else { return }
        let containerView = transitionContext.containerView
        //设置toVc的view的frame,fromVc就是当前界面上的vc就不需要设置和添加了
        toView.frame = CGRect(x: screen.width, y: 64, width: screen.width, height: screen.height - 64)
        containerView.addSubview(toView)
        //执行动画
        UIView.animate(withDuration: self.transitionDuration(using: transitionContext), animations: {
            fromView.alpha = 0.8
            fromView.transform = CGAffineTransform.init(scaleX: 0.9, y: 0.9)
            toView.frame = CGRect(x: 0, y: 64, width: self.screen.width, height: self.screen.height - 64)
        }) { (finish) in
            //!transitionContext.transitionWasCancelled 转场动画是否被取消(手势交互需要这样写,如果不需要手势的话直接写成true就可以了)
            //transitionContext.completeTransition这个方法在动画执行完后必需调用
            transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
        }
    }
    //pop
    fileprivate func popAnimation(using transitionContext: UIViewControllerContextTransitioning) {
        
        guard let fromView = transitionContext.view(forKey: UITransitionContextViewKey.from),
            let toView = transitionContext.view(forKey: UITransitionContextViewKey.to) else { return }
        let containerView = transitionContext.containerView
        
        toView.transform = CGAffineTransform.init(scaleX: 0.9, y: 0.9)
        toView.alpha = 0.8
        containerView.insertSubview(toView, at: 0)
        
        UIView.animate(withDuration: self.transitionDuration(using: transitionContext), animations: {
            toView.alpha = 1
            toView.transform = CGAffineTransform.identity
            fromView.frame = CGRect(x: self.screen.width, y: 64, width: self.screen.width, height: self.screen.height - 64)
        }) { (finish) in
            transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
        }
        
    }

4、在需要动画的控制中设置代理,实现代理方法

    \\每次显示这个控制器的view的时候都重新设置代理,不这样的做的话pop回来,下次在push的时候就不会去执行返回动画的代理了
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        self.navigationController?.delegate = self
    }

    extension ViewController: UINavigationControllerDelegate {
    
    func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationControllerOperation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        if operation == .push {
            return TransitionAnimations.init(type: .push)
        }else if operation == .pop {
            return TransitionAnimations.init(type: .pop)
        }else {
            return nil
        }
    }
}

5、手势交互

手势交互实现起来还是比较方便的,苹果已经为我们封装好了相关的方法了,我们只需要添加一个手势来更新相关参数就可以了
    //在viewDidLoad中实现
    //声明一个交互对象
    var interactiveTransition: UIPercentDrivenInteractiveTransition?

    func initializeInterface() {
        self.navigationController?.delegate = self
        view.backgroundColor = UIColor.yellow
        let panGesture: UIPanGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(respondsToPanGesture))
        view.addGestureRecognizer(panGesture)
    }
    
    fileprivate func respondsToPanGesture(pan: UIPanGestureRecognizer) {
        let moveX = pan.translation(in: pan.view).x
        let percent = moveX / screen.width
        switch pan.state {
        case .began:
            interactiveTransition = UIPercentDrivenInteractiveTransition()
            navigationController?.popViewController(animated: true)
        case .changed:
            //根据百分比更新交互
            interactiveTransition?.update(percent)
        case .ended:
            if percent > 0.5 {
                interactiveTransition?.finish()
            }else {
                interactiveTransition?.cancel()
            }
            interactiveTransition = nil
        default:
            print("default")
        }
        
    }

最后在该控制器中实现navigationdelegate的方法

extension BViewController: UINavigationControllerDelegate {
    
    func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationControllerOperation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        if operation == .push {
            return TransitionAnimations.init(type: .push)
        }else if operation == .pop {
            return TransitionAnimations.init(type: .pop)
        }else {
            return nil
        }
    }
    
    func navigationController(_ navigationController: UINavigationController, interactionControllerFor animationController: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
        // 返回交互对象
        return interactiveTransition
    }
    
}

Demo 地址

相关文章

网友评论

      本文标题:自定义控制器转场动画

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