viewController transitions

文章目录
  1. 1. 前言
  2. 2. transitions 结构
    1. 2.1. Transitioning Delegate
    2. 2.2. Animation Controller
  3. 3. 简述
  4. 4. 给 dismiss 添加手势交互

前言

iOS 系统提供了像 push, pop, cover vertically 这样的 ViewController 过渡,该篇分析如何自定义自己的 ViewController transitions。为啥要写这个因为“看着很麻烦”,比起 push,pop,present 来说……

思考:

  1. ViewController 的 过渡流程步骤是什么样的
  2. 都有什么组成,他们之间的逻辑是什么样的
  3. 用户如何使用

过渡的过程,应该有:

  1. 负责触发事件
  2. 负责管理过渡动画
  3. 负责过渡相关的ViewControllers,Views

transitions 结构

transitioning API 一组 protocols 结合,允许我们自定义化,使用一个现有的 transition 对象 or 自己定义一个新的。
transitioning API

Q: 每个协议的责任是什么?
Q: 执行步骤是什么

Transitioning Delegate

每个 ViewController 都有对象—— transitioningDelegate: UIViewControllerTransitioningDelegate 用来得到 Animation controller 对象

1
2
3
4
5
extension CardViewController: UIViewControllerTransitioningDelegate {
func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return FlipPresentAnimationController(originFrame: cardView.frame)
}
}

当你 present or dismiss ViewController 的时候,UIKit 会向 transitioningDelegate 要你自定义的 Animation controller 来替代默认动画。你要做的是实现 UIViewControllerTransitioningDelegate 代理方法返回 Animation controller

Animation Controller

用来做过渡动画,他实现自 UIViewControllerAnimatedTransitioning

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58

class FlipPresentAnimationController: NSObject,UIViewControllerAnimatedTransitioning {
// 设置动画时间
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return 0.6
}
// 从UIViewControllerContextTransitioning 中得到 toVc & fromVc,对其制作动画
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
//1 通过 transitionContext 得到 fromVC 是触发 present的VC,toVC 是要被呈现的 VC,snapShot,是toVC 呈现以后的view 截图用于做动画
guard let fromVC = transitionContext.viewController(forKey: .from),
let toVC = transitionContext.viewController(forKey: .to),
let snapshot = toVC.view.snapshotView(afterScreenUpdates: true)
else {
return
}
//containerView 是过渡过程中的容器 view
let containerView = transitionContext.containerView
let finalFrame = transitionContext.finalFrame(for: toVC)

snapshot.frame = originFrame
snapshot.layer.cornerRadius = CardViewController.cardCornerRadius
snapshot.layer.masksToBounds = true

containerView.addSubview(toVC.view)
containerView.addSubview(snapshot)
toVC.view.isHidden = true

AnimationHelper.perspectiveTransform(for: containerView)
snapshot.layer.transform = AnimationHelper.yRotation(.pi / 2)
let duration = transitionDuration(using: transitionContext)

// 对 view 做动画处理
UIView.animateKeyframes(
withDuration: duration,
delay: 0,
options: .calculationModeCubic,
animations: {
UIView.addKeyframe(withRelativeStartTime: 0.0, relativeDuration: 1/3) {
fromVC.view.layer.transform = AnimationHelper.yRotation(-.pi / 2)
}

UIView.addKeyframe(withRelativeStartTime: 1/3, relativeDuration: 1/3) {
snapshot.layer.transform = AnimationHelper.yRotation(0.0)
}

UIView.addKeyframe(withRelativeStartTime: 2/3, relativeDuration: 1/3) {
snapshot.frame = finalFrame
snapshot.layer.cornerRadius = 0
}
},
completion: { _ in
toVC.view.isHidden = false
snapshot.removeFromSuperview()
fromVC.view.layer.transform = CATransform3DIdentity
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
})
}
}

简述

对于需要自定义的 transitioning 的 ViewController,CustomViewController

  1. CustomViewController: UIViewControllerTransitioningDelegate 实现协议
  2. 实现协议的
1
2
animationController(presented:, presenting:, source:) -> UIViewControllerAnimatedTransitioning
animationController(dismiss:) -> UIViewControllerAnimatedTransitioning
  1. 自定义 Animation Controller(CustomeAnimationController: UIViewControllerAnimatedTransitioning)
  2. 实现 UIViewControllerAnimatedTransitioning 中的
1
2
transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval
animateTransition(transitionContext:)

给 dismiss 添加手势交互

Custom UIViewController Transitions: Getting Started