來源
https://developer.apple.com/library/content/featuredarticles/ViewControllerPGforiPhoneOS/CustomizingtheTransitionAnimations.html#//apple_ref/doc/uid/TP40007457-CH16-SW1
過渡動畫是把兩個controller的內容交換。有兩種交換類型:
- presentations:加一個新的view controller到你app的 view controller層級。
- dismissals:從app里移除一個或多個view controller。
the transitioning delegate
transitioning delegate是你自定義呈現和過渡動畫的起點。the transitioning delegate是一個你定義的并遵守UIViewControllerTransitioningDelegate協議的對象。它提供給UIKit以下對象:
- animator objects:它是創建動畫,并用來展示或隱藏一個view controller的view.這個transitioning delegate可以分別提供animator object 給presenting 和dismissing. animator object 遵守UIViewControllerAnimatedTransitioning協議。
- interactive animator objects:這個對象可以用事件或手勢和驅動自定義動畫的時間。它遵守UIViewControllerInteractiveTransitioning協議。
最簡單的創建一個interaction animator 是繼承UIPercentDrivenInteractiveTransition 類,并加事件處理代碼到你的子類里。這個類控制了你的動畫時間(這個動畫是用你已存在的animator objects創建的)。如果你要自己創建你自己的interaction animator,你必須自己來渲染動畫里的每一幀(還是不要這樣做--PPF)。 - 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就會執行以下步驟:
- UIKit 會調用transitioning delegate的interactionControllerForPresentation:方法去看一下是否有一個interactive animator(交互動畫對象)有效。如果是返回nil,UIKit就會執行一個沒有用戶交互的動畫。
- UIKit會調用animator object的 transitionDuration:得到動畫持續時間。
- UIKit會調用以下一個合適的方法開始動畫:
- 沒有interactive animations,UIKit會調用 animator 對象的 animateTransition:。
- 有interactive animations,UIKit會調用interactive animator對象的 startInteractiveTransition:
- UIKit會等著,等著animator對象的context transitioning對象調用 completeTransition: 。
你自己的自定義的animator會在動畫結束后調用這個方法,一般會在動畫結束塊里(completion block)。調用這個方法來結束過渡,并且讓UIKit知道這可以調用presentViewController:animated:completion: 的completion handler(結束塊)和調用animator 對象自己的animationEnded:方法。
當dismissing一個view controller時,UIKit會調用你的transitioning delegate的animationControllerForDismissedController:并執行以下步驟:
- UIKit調用transition delegate的interactionControllerForDismissal:方法去看一下是否有一個有效的interactive animator(交互動畫對象)。如果是返回nil,UIKit就會執行一個沒有用戶交互的動畫。
- UIKit會調用animator object的 transitionDuration:得到動畫持續時間。
- UIKit會調用以下一個合適的方法開始動畫:
- 沒有interactive animations,UIKit會調用 animator 對象的 animateTransition:。
- 有interactive animations,UIKit會調用interactive animator對象的
startInteractiveTransition:
- 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的活動方法里加以下步聚:
- 創建一個你要呈現的view controller.
- 創建你自己的transitioning delegate對象,并把它賦給新view controller的
transitioningDelegate
屬性。這個transitioning delegate的方法會在需要的時候提供你自定義的animator對象。 - 調用
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;
}