IOS实战之自定义转场动画详解

2020-01-14 20:18:51丽君

如果获取到了不是nil的对象,那么UIKit不会调用animator的animateTransition方法,而是调用交互式控制器(还记得前面介绍动画代理的示意图么,交互式动画控制器和animator是平级关系)的startInteractiveTransition:方法。

所谓的交互式动画,通常是基于手势驱动,产生一个动画完成的百分比来控制动画效果(文章开头的gif中第二个动画效果)。整个动画不再是一次性、连贯的完成,而是在任何时候都可以改变百分比甚至取消。这需要一个实现了UIPercentDrivenInteractiveTransition协议的交互式动画控制器和animator协同工作。这看上去是一个非常复杂的任务,但UIKit已经封装了足够多细节,我们只需要在交互式动画控制器和中定义一个时间处理函数(比如处理滑动手势),然后在接收到新的事件时,计算动画完成的百分比并且调用updateInteractiveTransition来更新动画进度即可。

用下面这段代码简单表示一下整个流程(删除了部分细节和注释,请不要以此为正确参考),完整的代码请参考demo中的Interactivity文件夹:

 


// 这个相当于fromViewController
class InteractivityFirstViewController: UIViewController {
   // 这个相当于toViewController
  lazy var interactivitySecondViewController: InteractivitySecondViewController = InteractivitySecondViewController()
  // 定义了一个InteractivityTransitionDelegate类作为代理
  lazy var customTransitionDelegate: InteractivityTransitionDelegate = InteractivityTransitionDelegate()

  override func viewDidLoad() {
    super.viewDidLoad()
    setupView() // 主要是一些UI控件的布局,可以无视其实现细节

    /// 设置动画代理,这个代理比较复杂,所以我们新建了一个代理对象而不是让self作为代理
    interactivitySecondViewController.transitioningDelegate = customTransitionDelegate
  }

  // 触发手势时,也会调用animationButtonDidClicked方法
  func interactiveTransitionRecognizerAction(sender: UIScreenEdgePanGestureRecognizer) {
    if sender.state == .Began {
      self.animationButtonDidClicked(sender)
    }
  }

  func animationButtonDidClicked(sender: AnyObject) {
    self.presentViewController(interactivitySecondViewController, animated: true, completion: nil)
  }
}

非交互式的动画代理只需要为present和dismiss提供animator即可,但是在交互式的动画代理中,还需要为present和dismiss提供交互式动画控制器:


class InteractivityTransitionDelegate: NSObject, UIViewControllerTransitioningDelegate {
  func animationControllerForPresentedController(presented: UIViewController, presentingController presenting: UIViewController, sourceController source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
    return InteractivityTransitionAnimator(targetEdge: targetEdge)
  }

  func animationControllerForDismissedController(dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
    return InteractivityTransitionAnimator(targetEdge: targetEdge)
  }

  /// 前两个函数和淡入淡出demo中的实现一致
  /// 后两个函数用于实现交互式动画

  func interactionControllerForPresentation(animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
    return TransitionInteractionController(gestureRecognizer: gestureRecognizer, edgeForDragging: targetEdge)
  }

  func interactionControllerForDismissal(animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
    return TransitionInteractionController(gestureRecognizer: gestureRecognizer, edgeForDragging: targetEdge)
  }
}