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

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

动画的关键在于animator如何实现,它实现了UIViewControllerAnimatedTransitioning协议,至少需要实现两个方法,我建议您仔细阅读animateTransition方法中的注释,它是整个动画逻辑的核心:

 


class HalfWaySpringAnimator: NSObject, UIViewControllerAnimatedTransitioning {
  /// 设置动画的持续时间
  func transitionDuration(transitionContext: UIViewControllerContextTransitioning?) -> NSTimeInterval {
    return 2
  }

  /// 设置动画的进行方式,附有详细注释,demo中其他地方的这个方法不再解释
  func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
    let fromViewController = transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey)
    let toViewController = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey)
    let containerView = transitionContext.containerView()

    // 需要关注一下from/to和presented/presenting的关系
    // For a Presentation:
    //   fromView = The presenting view.
    //   toView  = The presented view.
    // For a Dismissal:
    //   fromView = The presented view.
    //   toView  = The presenting view.

    var fromView = fromViewController?.view
    var toView = toViewController?.view

    // iOS8引入了viewForKey方法,尽可能使用这个方法而不是直接访问controller的view属性
    // 比如在form sheet样式中,我们为presentedViewController的view添加阴影或其他decoration,animator会对整个decoration view
    // 添加动画效果,而此时presentedViewController的view只是decoration view的一个子视图
    if transitionContext.respondsToSelector(Selector("viewForKey:")) {
      fromView = transitionContext.viewForKey(UITransitionContextFromViewKey)
      toView = transitionContext.viewForKey(UITransitionContextToViewKey)
    }

    // 我们让toview的origin.y在屏幕的一半处,这样它从屏幕的中间位置弹起而不是从屏幕底部弹起,弹起过程中逐渐变为不透明
    toView?.frame = CGRectMake(fromView!.frame.origin.x, fromView!.frame.maxY / 2, fromView!.frame.width, fromView!.frame.height)
    toView?.alpha = 0.0

    // 在present和,dismiss时,必须将toview添加到视图层次中
    containerView?.addSubview(toView!)

    let transitionDuration = self.transitionDuration(transitionContext)
    // 使用spring动画,有弹簧效果,动画结束后一定要调用completeTransition方法
    UIView.animateWithDuration(transitionDuration, delay: 0, usingSpringWithDamping: 0.6, initialSpringVelocity: 0, options: .CurveLinear, animations: { () -> Void in
      toView!.alpha = 1.0   // 逐渐变为不透明
      toView?.frame = transitionContext.finalFrameForViewController(toViewController!)  // 移动到指定位置
      }) { (finished: Bool) -> Void in
        let wasCancelled = transitionContext.transitionWasCancelled()
        transitionContext.completeTransition(!wasCancelled)
    }
  }
}