自定義過渡動畫

來源

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

過渡動畫是把兩個controller的內容交換。有兩種交換類型:

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

the transitioning delegate

transitioning delegate是你自定義呈現和過渡動畫的起點。the transitioning delegate是一個你定義的并遵守UIViewControllerTransitioningDelegate協議的對象。它提供給UIKit以下對象:

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

給 view controller的transitioningDelegate屬性賦值一個transitioning delegate,用于告訴UIKit 你要執行一個自定義的過渡和展示方式。你的delegate可以選擇提供哪個對象。如果你不提供animator objects,UIKit會使用標準的過渡動畫,在view controller的modalTransitionStyle的屬性里。

只有當view controller的modalpresentationStype的屬性被設為UIModelPresentationCustom時,自定義的presentation controller才會被使用。


自定義動畫的調用順序。

當被呈現的view controller的transitioningDelegate屬性包含一個有效值時,UIKit就會用你提供的動畫對象(animator objects)來呈現那個view controller.當它已經準備好呈現時,UIKit就會調用transition delegate里的 animationControllerForPresentedController:presentingController:sourceController:方法來接收一個動畫對象(animator object).如果對象有效,UIKit就會執行以下步驟:

  1. UIKit 會調用transitioning delegate的interactionControllerForPresentation:方法去看一下是否有一個interactive animator(交互動畫對象)有效。如果是返回nil,UIKit就會執行一個沒有用戶交互的動畫。
  2. UIKit會調用animator object的 transitionDuration:得到動畫持續時間。
  3. UIKit會調用以下一個合適的方法開始動畫:
  • 沒有interactive animations,UIKit會調用 animator 對象的 animateTransition:。
  • 有interactive animations,UIKit會調用interactive animator對象的 startInteractiveTransition:
  1. UIKit會等著,等著animator對象的context transitioning對象調用 completeTransition: 。
    你自己的自定義的animator會在動畫結束后調用這個方法,一般會在動畫結束塊里(completion block)。調用這個方法來結束過渡,并且讓UIKit知道這可以調用presentViewController:animated:completion: 的completion handler(結束塊)和調用animator 對象自己的animationEnded:方法。

當dismissing一個view controller時,UIKit會調用你的transitioning delegate的animationControllerForDismissedController:并執行以下步驟:

  1. UIKit調用transition delegate的interactionControllerForDismissal:方法去看一下是否有一個有效的interactive animator(交互動畫對象)。如果是返回nil,UIKit就會執行一個沒有用戶交互的動畫。
  2. UIKit會調用animator object的 transitionDuration:得到動畫持續時間。
  3. UIKit會調用以下一個合適的方法開始動畫:
    • 沒有interactive animations,UIKit會調用 animator 對象的 animateTransition:。
  • 有interactive animations,UIKit會調用interactive animator對象的 startInteractiveTransition:
  1. UIKit會等著,等著animator對象的context transitioning對象調用 completeTransition:
    你自己的自定義的animator會在動畫結束后調用這個方法,一般會在動畫結束塊里(completion block)。調用這個方法來結束過渡,并且讓UIKit知道這可以調用presentViewController:animated:completion:的completion handler(結束塊)和調用animator 對象自己的animationEnded:方法。
注意:

必須在你的動畫結束時調用completeTransition:。UIKit不會結束過渡過程,并因此把控制權還給你的app,直到你調用了這個方法。

The Transitioning Context Object

注意:

當你設置自定義的動畫時,一定要只使用在transitioning context對象里的對象和數據,而不是你自己管理的緩存數據。過渡是會發生多種多樣的情況下,而有一些情況可能會改變動畫參數。the transitioning context對象保證會有你需要的正確信息來執行動畫,然而你自己的緩存的信息有可能在動畫方法被調用時就失效了(不新鮮了)

Presenting a View Controller Using Custom Animations

用自定義的動畫要呈現一個新的view controller, 在已存在的view controller的活動方法里加以下步聚:

  1. 創建一個你要呈現的view controller.
  2. 創建你自己的transitioning delegate對象,并把它賦給新view controller的transitioningDelegate屬性。這個transitioning delegate的方法會在需要的時候提供你自定義的animator對象。
  3. 調用presentViewController:animated:completion:方法,呈現新的view controller。

試一下

如果已經有一個segue了,以上的步驟是否可以在prepareForSegue:里進行。

Getting the Animation Parameters

  • 調用viewControllerForKey:分別得到“from"和”to“兩個view controller.
  • 調用containerView方法得到動畫的父視圖。加入所有的關鍵subView到這里。比如:加入presented view controller的view。
  • 調用viewForKey:方法得到要加入或移除的view.
  • 調用finalFrameForViewController:方法得到被加入或移除的view的最終frame。

  • from:總是在過渡開始時在屏幕上的view controller.
  • to:總是在過渡結束時在屏幕上的view controller.

動畫的主要目標:

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

Creating the Transition Animations

  • presentation animations:

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

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

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

  • 把"to"view作為一個subview加入container view.

  • 創建動畫:

  • 在你的動畫塊里,在container view里把"to"view移動到它的最終位置。設置最終值到它的其它屬性里。

  • 在動畫結束塊里(completion block)里,調用completeTransition:方法,執行其它清理工作。

  • Dismissal animations:

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

  • 計算"from"view的結束位置。這個presented view controller里的view 現在是要被dismissed。

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

  • 創建動畫:

  • 在你的動畫塊里,在container view里把"from"view移動到它的最終位置。設置最終值到它的其它屬性里。

  • 在動畫結束塊里(completion block)里,移除"from" view,調用completeTransition:方法,執行其它清理工作。

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) {
        //獲取關聯的對象
        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)!
        
        //設置一些動畫的變量。
        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;
    }

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,117評論 6 537
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,860評論 3 423
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,128評論 0 381
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,291評論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,025評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,421評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,477評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,642評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,177評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,970評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,157評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,717評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,410評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,821評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,053評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,896評論 3 395
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,157評論 2 375

推薦閱讀更多精彩內容