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 視圖控制器轉場詳解

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容