iOS-自定义控制器转场动画(present/dismiss)

一、转场动画类型

iOS控制器转场动画类型可以分为非交互式转场动画和交互式转场动画。

二、转场动画分析

2.1、转场代理

自定义控制器转场动画需要重新实现控制器的转场代理方法UIViewControllerTransitioningDelegate。

//控制器present时执行的代理方法
func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning?

//控制器dismiss时执行的代理方法
func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning?

//交互式转场时控制器present执行的代理方法
func interactionControllerForPresentation(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning?

//交互式转场是控制器dismiss执行的代理方法
func interactionControllerForDismissal(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning?

//定义控制器转场过程
func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController?

自定义转场代理方法需要将PresentedViewController的modalPresentationStyle设为custom或者fullScreen下才执行。
custom和fullScreen的区别是:
fullScreen模式:控制器presentation 后,presentingView 会被主动移出视图结构,在 dismissal 中 presentingView 是 toView 的角色,其将会重新加入 containerView 中。
custom模式:在 presentation 后,presentingView 未被移出视图结构,在 dismissal 中,不要将 presentingView 加入 containerView 中,否则本来可见的 presentingView 将会被移除出自身所处的视图结构消失不见。

2.2、动画协议

控制器的动画协议UIViewControllerAnimatedTransitioning主要负责动画的执行过程。

//动画的执行时间
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval

//动画的具体执行过程在这个方法中实现
func animateTransition(using transitionContext: UIViewControllerContextTransitioning)

//是否使能动画
func animationEnded(_ transitionCompleted: Bool)

2.3、动画的承载

UIViewControllerContextTransitioning协议承载了动画的重要参数,在转场之前可以得到遵守写的的对象transitionContext,通过这个对象可以获取动画所需的参数。

//容器View,承载转场动画
public var containerView: UIView { get }

//获取参与转场的控制器
public func viewController(forKey key: UITransitionContextViewControllerKey) -> UIViewController?

//获取转场视图
public func view(forKey key: UITransitionContextViewKey) -> UIView?

三、转场动画实现

3.1、非交互式转场动画

3.1.1、转场动画的具体实现

创建一个遵守UIViewControllerAnimatedTransitioning协议的类MaskAnimatedTransition,用于实现具体的动画。
实现协议方法:

func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
    return 0.3
}

func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
    if modalType == .present {
        presentAnimateTransition(using: transitionContext)
    } else {
        dismissAnimateTransition(using: transitionContext)
    }
}

创建两个私有的方法,分别实现控制器present和dismiss转场时的动画:

private func presentAnimateTransition(using transitionContext: UIViewControllerContextTransitioning) {
     ......
}

private func dismissAnimateTransition(using transitionContext: UIViewControllerContextTransitioning) {
    ......
}

创建一个枚举类型,用于判断控制器的转场类型

enum MaskAnimatedTransitionModalType {
    case present
    case dismiss
}
3.1.2、重写转场代理协议

创建一个新的类,遵守控制器转场代理协议UIViewControllerTransitioningDelegate,实现协议方法。

func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
    return MaskAnimatedTransition(.present)
}

func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
    return MaskAnimatedTransition(.dismiss)
}
3.1.3、设置转场代理

创建一个全局的转场代理:

let maskTransitionDelegate = MaskTransitionDelegate()

将控制器的转场代理transitioningDelegate设为maskTransitionDelegate,将控制器的modalPresentationStyle设为custom,即可进行控制器的模态转场。
上述控制的转场动画是一个渐入渐出的动画类型,动画时间为0.3秒。

3.2交互式转场动画的实现

交互式转场动画相对非交互动画而言要多实现一个控制器交互过渡协议UIViewControllerInteractiveTransitioning。

3.2.1、交互式转场动画的实现

实现开始交互协议方法:

func startInteractiveTransition(_ transitionContext: UIViewControllerContextTransitioning) {
    if modalType == .present {
        presentStartInteractiveTransition(transitionContext)
    } else {
        dismissStartInteractiveTransition(transitionContext)
    }
}

在presentedViewController上添加一个手势UIPanGestureRecognizer,用于交互:

var presentedViewController: UIViewController? = nil {
    didSet {
        let panGR = UIPanGestureRecognizer(target: self, action: #selector(panGestureReconizerAction(_:)))
        self.presentedViewController?.view.addGestureRecognizer(panGR)
    }
}

在交互方法中实现具体的动画:

@objc private func panGestureReconizerAction(_ panGR: UIPanGestureRecognizer) {
    ......
}
3.2.2、实现交互转场代理方法

在重写转场代理时,实现两个交互转场代理方法:

public func interactionControllerForPresentation(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
    return nil
}

public func interactionControllerForDismissal(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
    interaction.modalType = .dismiss
    return interaction.isInteraction ? interaction : nil
}
3.2.3、设置转场代理

创建一个全局的转场代理:

let maskTransitionDelegate = MaskTransitionDelegate()

将控制器的转场代理transitioningDelegate设为maskTransitionDelegate,将控制器的modalPresentationStyle设为custom,在控制器dismiss时,即可进行交互转场。

Demo

推荐文章:
iOS 视图控制器转场详解

你可能感兴趣的:(iOS-自定义控制器转场动画(present/dismiss))