IBAnimatable转场动画-Explode学习

阅读源码后可以发现,和一般自定义转场动画一致,新建继承 NSObject 子类,遵守 UIViewControllerAnimatedTransitioning 协议。

实现两个代理方法:

  • 返回动画持续时间代理:
func transitionDuration(transitionContext: UIViewControllerContextTransitioning?) -> NSTimeInterval {
        return 0.75
}
  • 自定义动画代理:
func animateTransition(transitionContext: UIViewControllerContextTransitioning) {    
      // 自定义动画函数
}

参数 transitionContext 可以可以通过 func viewForKey(key: String) -> UIView? / public func viewControllerForKey(key: String) -> UIViewController? 取出转场动画的对应 fromView/toView / formViewController/toViewController 对应的 key 值:

viewForKey:
UITransitionContextFromViewKey
UITransitionContextToViewKey

viewControllerForKey:
UITransitionContextFromViewControllerKey
UITransitionContextToViewControllerKey

Explode 动画中主要在于屏幕快照的获取以及快照的区域分剪,核心代码:

// 获取 fromView 的快照
let fromViewSnapshot = fromView.snapshotViewAfterScreenUpdates(false)

        // 将快照剪切成小块加到 containerView 上
        for x in 0.0.stride(to: Double(size.width), by: Double(size.width / xFactor)) {
            for y in 0.0.stride(to: Double(size.height), by: Double(size.height / yFactor)) {
                let snapshotRegion = CGRect(x: CGFloat(x), y: CGFloat(y), width: size.width / xFactor, height: size.height / yFactor)

                // 按所给区域获得快照的小块
                let snapshot = fromViewSnapshot.resizableSnapshotViewFromRect(snapshotRegion, afterScreenUpdates: false, withCapInsets: UIEdgeInsetsZero)
                // 主要是设置位置
                snapshot.frame = snapshotRegion
                // 将拼成的 fromView 快照加到 containerView的最顶层
                containerView.addSubview(snapshot)
                snapshots.append(snapshot)
            }
        }

// 将 fromView 隐藏
containerView.sendSubviewToBack(fromView)

剩下的就是对 每一个小块的动画处理,并在动画结束后调用:

ransitionContext.completeTransition(!transitionContext.transitionWasCancelled())

这都很简单,难的是如何结合手势使用,这是最值得学习的地方,理解不深,可以 clone 源码 学习。

实现过程主要是对 UIPercentDrivenInteractiveTransition 的学习使用,和 IBAnimatable 的实现不同,我们采用 NavigationController 管理界面,在 FirstViewControllerfunc viewWillAppear(animated: Bool) {} 内设置代理: navigationController?.delegate = self
(如果在方法: func viewDidLoad() {} 设置代理会导致转场取消后无法再次进行自定义动画转场)
实现代理方法:

extension FirstViewController: UINavigationControllerDelegate {
    func navigationController(navigationController: UINavigationController, animationControllerForOperation operation: UINavigationControllerOperation, fromViewController fromVC: UIViewController, toViewController toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        if operation == UINavigationControllerOperation.Push {
            // ExplodeAnimator 即为自定义的转场动画
            return ExplodeAnimator()
        } else {
            return nil
        }
    }
}

之后就是对 SecondViewController 内进行自定义手势 popViewController :
首先对 view 添加返回手势:

view.addGestureRecognizer({
            let pan = UIScreenEdgePanGestureRecognizer(target: self, action: #selector(SecondViewController.pan(_:)))
            pan.edges = UIRectEdge.Left
            return pan
        }())

手势回调方法:

func pan(edgePan: UIScreenEdgePanGestureRecognizer) {

        let progress = edgePan.translationInView(self.view).x / self.view.bounds.width

        if edgePan.state == UIGestureRecognizerState.Began {
            self.percentDrivenTransition = UIPercentDrivenInteractiveTransition()
            self.navigationController?.popViewControllerAnimated(true)
        } else if edgePan.state == UIGestureRecognizerState.Changed {
            self.percentDrivenTransition?.updateInteractiveTransition(progress)
        } else if edgePan.state == UIGestureRecognizerState.Cancelled || edgePan.state == UIGestureRecognizerState.Ended {
            if progress > 0.5 {
                self.percentDrivenTransition?.finishInteractiveTransition()
            } else {
                self.percentDrivenTransition?.cancelInteractiveTransition()
            }
            self.percentDrivenTransition = nil
        }
    }

同样,在 SecondViewControllerfunc viewWillAppear(animated: Bool) {} 方法内设置代理: navigationController!.delegate = self,区别只是在于多实现一个代理方法:

extension SecondViewController: UINavigationControllerDelegate {

    func navigationController(navigationController: UINavigationController, animationControllerForOperation operation: UINavigationControllerOperation, fromViewController fromVC: UIViewController, toViewController toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        if operation == UINavigationControllerOperation.Pop {
            return ExplodeAnimator()
        } else {
            return nil
        }
    }

    func navigationController(navigationController: UINavigationController, interactionControllerForAnimationController animationController: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
        if animationController is ExplodeAnimator {
            return self.percentDrivenTransition
        } else {
            return nil
        }
    }
}

大功告成,
效果展示:

Demo

代码地址: CodeDemo。
IBAnimatable 源码的实现基于高度的封装,这也是望尘莫及的地方。

你可能感兴趣的:(IBAnimatable转场动画-Explode学习)