自定義過(guò)渡動(dòng)畫(huà)

來(lái)源

https://developer.apple.com/library/content/featuredarticles/ViewControllerPGforiPhoneOS/CustomizingtheTransitionAnimations.html#//apple_ref/doc/uid/TP40007457-CH16-SW1

過(guò)渡動(dòng)畫(huà)是把兩個(gè)controller的內(nèi)容交換。有兩種交換類型:

  1. presentations:加一個(gè)新的view controller到你app的 view controller層級(jí)。
  2. dismissals:從app里移除一個(gè)或多個(gè)view controller。

the transitioning delegate

transitioning delegate是你自定義呈現(xiàn)和過(guò)渡動(dòng)畫(huà)的起點(diǎn)。the transitioning delegate是一個(gè)你定義的并遵守UIViewControllerTransitioningDelegate協(xié)議的對(duì)象。它提供給UIKit以下對(duì)象:

  1. animator objects:它是創(chuàng)建動(dòng)畫(huà),并用來(lái)展示或隱藏一個(gè)view controller的view.這個(gè)transitioning delegate可以分別提供animator object 給presenting 和dismissing. animator object 遵守UIViewControllerAnimatedTransitioning協(xié)議。
  2. interactive animator objects:這個(gè)對(duì)象可以用事件或手勢(shì)和驅(qū)動(dòng)自定義動(dòng)畫(huà)的時(shí)間。它遵守UIViewControllerInteractiveTransitioning協(xié)議。
    最簡(jiǎn)單的創(chuàng)建一個(gè)interaction animator 是繼承UIPercentDrivenInteractiveTransition 類,并加事件處理代碼到你的子類里。這個(gè)類控制了你的動(dòng)畫(huà)時(shí)間(這個(gè)動(dòng)畫(huà)是用你已存在的animator objects創(chuàng)建的)。如果你要自己創(chuàng)建你自己的interaction animator,你必須自己來(lái)渲染動(dòng)畫(huà)里的每一幀(還是不要這樣做--PPF)。
  3. presentation controller: 當(dāng)一個(gè)view controller展示在屏幕上時(shí), presentation controller管理著展示方式。系統(tǒng)為內(nèi)置的展示類型提供了presentation controller,并且你也可以為您自己的展示類型提供自定義的presentation controller(這部分在下一篇會(huì)講--PPF)。

給 view controller的transitioningDelegate屬性賦值一個(gè)transitioning delegate,用于告訴UIKit 你要執(zhí)行一個(gè)自定義的過(guò)渡和展示方式。你的delegate可以選擇提供哪個(gè)對(duì)象。如果你不提供animator objects,UIKit會(huì)使用標(biāo)準(zhǔn)的過(guò)渡動(dòng)畫(huà),在view controller的modalTransitionStyle的屬性里。

只有當(dāng)view controller的modalpresentationStype的屬性被設(shè)為UIModelPresentationCustom時(shí),自定義的presentation controller才會(huì)被使用。


自定義動(dòng)畫(huà)的調(diào)用順序。

當(dāng)被呈現(xiàn)的view controller的transitioningDelegate屬性包含一個(gè)有效值時(shí),UIKit就會(huì)用你提供的動(dòng)畫(huà)對(duì)象(animator objects)來(lái)呈現(xiàn)那個(gè)view controller.當(dāng)它已經(jīng)準(zhǔn)備好呈現(xiàn)時(shí),UIKit就會(huì)調(diào)用transition delegate里的 animationControllerForPresentedController:presentingController:sourceController:方法來(lái)接收一個(gè)動(dòng)畫(huà)對(duì)象(animator object).如果對(duì)象有效,UIKit就會(huì)執(zhí)行以下步驟:

  1. UIKit 會(huì)調(diào)用transitioning delegate的interactionControllerForPresentation:方法去看一下是否有一個(gè)interactive animator(交互動(dòng)畫(huà)對(duì)象)有效。如果是返回nil,UIKit就會(huì)執(zhí)行一個(gè)沒(méi)有用戶交互的動(dòng)畫(huà)。
  2. UIKit會(huì)調(diào)用animator object的 transitionDuration:得到動(dòng)畫(huà)持續(xù)時(shí)間。
  3. UIKit會(huì)調(diào)用以下一個(gè)合適的方法開(kāi)始動(dòng)畫(huà):
  • 沒(méi)有interactive animations,UIKit會(huì)調(diào)用 animator 對(duì)象的 animateTransition:。
  • 有interactive animations,UIKit會(huì)調(diào)用interactive animator對(duì)象的 startInteractiveTransition:
  1. UIKit會(huì)等著,等著animator對(duì)象的context transitioning對(duì)象調(diào)用 completeTransition: 。
    你自己的自定義的animator會(huì)在動(dòng)畫(huà)結(jié)束后調(diào)用這個(gè)方法,一般會(huì)在動(dòng)畫(huà)結(jié)束塊里(completion block)。調(diào)用這個(gè)方法來(lái)結(jié)束過(guò)渡,并且讓UIKit知道這可以調(diào)用presentViewController:animated:completion: 的completion handler(結(jié)束塊)和調(diào)用animator 對(duì)象自己的animationEnded:方法。

當(dāng)dismissing一個(gè)view controller時(shí),UIKit會(huì)調(diào)用你的transitioning delegate的animationControllerForDismissedController:并執(zhí)行以下步驟:

  1. UIKit調(diào)用transition delegate的interactionControllerForDismissal:方法去看一下是否有一個(gè)有效的interactive animator(交互動(dòng)畫(huà)對(duì)象)。如果是返回nil,UIKit就會(huì)執(zhí)行一個(gè)沒(méi)有用戶交互的動(dòng)畫(huà)。
  2. UIKit會(huì)調(diào)用animator object的 transitionDuration:得到動(dòng)畫(huà)持續(xù)時(shí)間。
  3. UIKit會(huì)調(diào)用以下一個(gè)合適的方法開(kāi)始動(dòng)畫(huà):
    • 沒(méi)有interactive animations,UIKit會(huì)調(diào)用 animator 對(duì)象的 animateTransition:。
  • 有interactive animations,UIKit會(huì)調(diào)用interactive animator對(duì)象的 startInteractiveTransition:
  1. UIKit會(huì)等著,等著animator對(duì)象的context transitioning對(duì)象調(diào)用 completeTransition: 。
    你自己的自定義的animator會(huì)在動(dòng)畫(huà)結(jié)束后調(diào)用這個(gè)方法,一般會(huì)在動(dòng)畫(huà)結(jié)束塊里(completion block)。調(diào)用這個(gè)方法來(lái)結(jié)束過(guò)渡,并且讓UIKit知道這可以調(diào)用presentViewController:animated:completion:的completion handler(結(jié)束塊)和調(diào)用animator 對(duì)象自己的animationEnded:方法。
注意:

必須在你的動(dòng)畫(huà)結(jié)束時(shí)調(diào)用completeTransition:。UIKit不會(huì)結(jié)束過(guò)渡過(guò)程,并因此把控制權(quán)還給你的app,直到你調(diào)用了這個(gè)方法。

The Transitioning Context Object

注意:

當(dāng)你設(shè)置自定義的動(dòng)畫(huà)時(shí),一定要只使用在transitioning context對(duì)象里的對(duì)象和數(shù)據(jù),而不是你自己管理的緩存數(shù)據(jù)。過(guò)渡是會(huì)發(fā)生多種多樣的情況下,而有一些情況可能會(huì)改變動(dòng)畫(huà)參數(shù)。the transitioning context對(duì)象保證會(huì)有你需要的正確信息來(lái)執(zhí)行動(dòng)畫(huà),然而你自己的緩存的信息有可能在動(dòng)畫(huà)方法被調(diào)用時(shí)就失效了(不新鮮了)

Presenting a View Controller Using Custom Animations

用自定義的動(dòng)畫(huà)要呈現(xiàn)一個(gè)新的view controller, 在已存在的view controller的活動(dòng)方法里加以下步聚:

  1. 創(chuàng)建一個(gè)你要呈現(xiàn)的view controller.
  2. 創(chuàng)建你自己的transitioning delegate對(duì)象,并把它賦給新view controller的transitioningDelegate屬性。這個(gè)transitioning delegate的方法會(huì)在需要的時(shí)候提供你自定義的animator對(duì)象。
  3. 調(diào)用presentViewController:animated:completion:方法,呈現(xiàn)新的view controller。

試一下

如果已經(jīng)有一個(gè)segue了,以上的步驟是否可以在prepareForSegue:里進(jìn)行。

Getting the Animation Parameters

  • 調(diào)用viewControllerForKey:分別得到“from"和”to“兩個(gè)view controller.
  • 調(diào)用containerView方法得到動(dòng)畫(huà)的父視圖。加入所有的關(guān)鍵subView到這里。比如:加入presented view controller的view。
  • 調(diào)用viewForKey:方法得到要加入或移除的view.
  • 調(diào)用finalFrameForViewController:方法得到被加入或移除的view的最終frame。

  • from:總是在過(guò)渡開(kāi)始時(shí)在屏幕上的view controller.
  • to:總是在過(guò)渡結(jié)束時(shí)在屏幕上的view controller.

動(dòng)畫(huà)的主要目標(biāo):

  • presentation:加入”to“view 到container view。
  • dismissal: 從container view里移除”from“view。

Creating the Transition Animations

  • presentation animations:

  • 使用viewControllerForKey:viewForKey:方法得到view controllers 和views。

  • 設(shè)置 "to"view的開(kāi)始位置。還有其它屬性的起始值(比如transform)。

  • finalFrameForViewController:方法得到"to"view的結(jié)束位置。

  • 把"to"view作為一個(gè)subview加入container view.

  • 創(chuàng)建動(dòng)畫(huà):

  • 在你的動(dòng)畫(huà)塊里,在container view里把"to"view移動(dòng)到它的最終位置。設(shè)置最終值到它的其它屬性里。

  • 在動(dòng)畫(huà)結(jié)束塊里(completion block)里,調(diào)用completeTransition:方法,執(zhí)行其它清理工作。

  • Dismissal animations:

  • 使用viewControllerForKey:viewForKey:方法得到view controllers 和views。

  • 計(jì)算"from"view的結(jié)束位置。這個(gè)presented view controller里的view 現(xiàn)在是要被dismissed。

  • 把"to"view作為一個(gè)subview加入container view.
    在過(guò)渡結(jié)束時(shí),presenting view controller的view("from" view)會(huì)被移除。在dismissal過(guò)程中,你必須把它加回到container view。

  • 創(chuàng)建動(dòng)畫(huà):

  • 在你的動(dòng)畫(huà)塊里,在container view里把"from"view移動(dòng)到它的最終位置。設(shè)置最終值到它的其它屬性里。

  • 在動(dòng)畫(huà)結(jié)束塊里(completion block)里,移除"from" view,調(diào)用completeTransition:方法,執(zhí)行其它清理工作。

import UIKit

class PresentationAnimator: NSObject,UIViewControllerAnimatedTransitioning {
    
    // true:presenting   false:dismissable
    let presenting:Bool
    
    init(presenting:Bool) {
        self.presenting = presenting
        super.init()
    }
    
    func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
        return 1.5
    }
    
    func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
        //獲取關(guān)聯(lián)的對(duì)象
        let containerView = transitionContext.containerView
        let fromVC  = transitionContext.viewController(forKey: .from)!
        let toVC    = transitionContext.viewController(forKey: .to)!
        
        let toV     = transitionContext.view(forKey: .to)!
        let fromV   = transitionContext.view(forKey: .from)!
        
        //設(shè)置一些動(dòng)畫(huà)的變量。
        let containerFrame = containerView.frame
        
        var toVStartFrame = transitionContext.initialFrame(for: toVC)
        let toVFinalFrame = transitionContext.finalFrame(for: toVC)
        
        var fromVFinalFrame = transitionContext.finalFrame(for: fromVC)
        
        if self.presenting {
            toVStartFrame.origin.x = containerFrame.width
            toVStartFrame.origin.y = containerFrame.height

            containerView.addSubview(toV)
            toV.frame = toVStartFrame
        }else{
            fromVFinalFrame.origin.x = containerFrame.width
            fromVFinalFrame.origin.y = containerFrame.height
            fromVFinalFrame.size.width = toV.frame.size.width
            fromVFinalFrame.size.height = toV.frame.size.height

            containerView.insertSubview(toV, at: 0)
            toV.frame = toVFinalFrame
        }
        
        
        UIView.animate(withDuration: transitionDuration(using: transitionContext), animations: { 
            if self.presenting{
                toV.frame = toVFinalFrame
            }else{
                fromV.frame = fromVFinalFrame
            }
        }) { (_) in
            let success = !transitionContext.transitionWasCancelled
            // After a failed presentation or successful dismissal, remove the view.
            if (self.presenting && !success) || (!self.presenting && success){
                toV.removeFromSuperview()
            }
            transitionContext.completeTransition(success)
        }
    }
}

中間還有一段interface animator 等以后再看看........


以上的東西也可以用在navigation controller上。push pop.

self.navigationController?.delegate = self
let percentDriven =  UIPercentDrivenInteractiveTransition()
extension ViewController:UINavigationControllerDelegate{
    func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationControllerOperation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        switch operation {
        case .push:
            //animator object
            return PresentationAnimator(presenting: true)
        case .pop:
            return PresentationAnimator(presenting: false)
        default:
            return nil
        }
    }
}

    func navigationController(_ navigationController: UINavigationController, interactionControllerFor animationController: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
        return percentDriven;
    }

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容