[TOC=5]
* * * * *
>原文鏈接 :[Customizing the Transition Animations](https://developer.apple.com/library/content/featuredarticles/ViewControllerPGforiPhoneOS/CustomizingtheTransitionAnimations.html#//apple_ref/doc/uid/TP40007457-CH16-SW1)
轉換動畫提供關于應用程序界面更改的視覺反饋。 UIKit 在呈現視圖控制器時提供了一組標準轉換樣式,可以使用自定義轉換來補充標準轉換。
### 第一節 轉換動畫流程
轉換動畫將一個視圖控制器的內容轉換為另一個視圖控制器的內容。有兩種類型的轉換:顯示和關閉。一個表示轉換將一個新的視圖控制器添加到應用程序的視圖控制器層次結構中,而一個被關閉的轉換將從層次結構中移除一個或多個視圖控制器。
實現轉換動畫需要很多對象。UIKit 提供了所有與轉換相關的對象的默認版本,可以自定義所有這些對象,或者一部分。如果選擇了合適的對象,那么應該能夠只使用少量的代碼來創建動畫。如果利用了 UIKit 提供的現有代碼,那么即使是包含交互的動畫也可以很容易地實現。
#### 1.1 轉換代理
轉換代理是轉換動畫和自定義顯示的起點。 轉換代理是一個對象,由你自定義并且遵受 `UIViewControllerTransitioningDelegate` 協議。它的工作是為 UIKit 提供以下對象:
* **Animator objects.** 動畫器對象負責創建用于顯示或隱藏視圖控制器視圖的動畫。轉換代理可以為呈現和關閉視圖控制器提供單獨的動畫器對象。動畫器對象遵守 [UIViewControllerAnimatedTransitioning](https://developer.apple.com/documentation/uikit/uiviewcontrolleranimatedtransitioning) 協議。
* **Interactive animator objects.** 交互式動畫器對象使用觸摸事件或手勢識別器來驅動自定義動畫的時間。 交互式動畫器遵守 [UIViewControllerInteractiveTransitioning](https://developer.apple.com/documentation/uikit/uiviewcontrollerinteractivetransitioning) 協議。
創建交互式動畫的最簡單的方法是繼承 [UIPercentDrivenInteractiveTransition](https://developer.apple.com/documentation/uikit/uipercentdriveninteractivetransition) 類并將事件處理代碼添加到您的子類中。 該類控制使用現有的動畫對象創建的動畫的時間。 如果您創建自己的交互式動畫,則必須自己渲染動畫的每一幀。
* **Presentation controller.** 顯示控制器管理視圖控制器在屏幕上顯示風格。 系統為內置顯示樣式提供顯示控制器,可以為自己的顯示樣式提供自定義顯示控制器。 有關創建自定義演顯示控制器的更多信息,請參閱 [Creating Custom Presentations](https://developer.apple.com/library/content/featuredarticles/ViewControllerPGforiPhoneOS/DefiningCustomPresentations.html#//apple_ref/doc/uid/TP40007457-CH25-SW1) 。
將轉換代理賦值視圖控制器的 [transitioningDelegate](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621421-transitioningdelegate) 屬性會告知 UIKit 要執行自定義轉換或顯示。 你的代理可以選擇它提供的對象。 如果不提供動畫對象,則 UIKit 根據視圖控制器的 [modalTransitionStyle](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621388-modaltransitionstyle) 屬性中選擇一個標準轉換動畫。
圖 10-1 顯示了轉換代理、動畫對象與呈現視圖控制器的關系。 僅當視圖控制器的 [modalPresentationStyle](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621355-modalpresentationstyle) 屬性設置為 [UIModalPresentationCustom](https://developer.apple.com/documentation/uikit/uimodalpresentationstyle/1621375-custom) 。
###### 圖 10-1 自定義顯示和動畫對象

有關如何實現轉換代理的信息,請參閱 [Implementing the Transitioning Delegate](https://developer.apple.com/library/content/featuredarticles/ViewControllerPGforiPhoneOS/CustomizingtheTransitionAnimations.html#//apple_ref/doc/uid/TP40007457-CH16-SW15)。有關轉換代理對象方法的更多信息,請參閱 [UIViewControllerTransitioningDelegate Protocol Reference](https://developer.apple.com/documentation/uikit/uiviewcontrollertransitioningdelegate) 。
#### 1.2 自定義動畫流程
當被呈現視圖控制器的 [transitioningDelegate](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621421-transitioningdelegate) 屬性包含有效對象時,UIKit 會使用提供的自定義動畫器對象呈現該視圖控制器。 在準備顯示效果時,UIKit 會調用轉換代理的 [animationControllerForPresentedController:presentsController:sourceController:](https://developer.apple.com/documentation/uikit/uiviewcontrollertransitioningdelegate/1622037-animationcontroller) 方法來檢索自定義動畫器對象。 如果有對象可用,UIKit 將執行以下步驟:
1. UIKit 調用轉換委托的 [interactionControllerForPresentation:](https://developer.apple.com/documentation/uikit/uiviewcontrollertransitioningdelegate/1622050-interactioncontrollerforpresenta) 方法來查看交互式動畫對象是否可用。 如果該方法返回 nil ,那么 UIKit 將執行無需用戶交互的動畫。
2. UIKit 調用動畫器對象的 [transitionDuration:](https://developer.apple.com/documentation/uikit/uiviewcontrolleranimatedtransitioning/1622032-transitionduration) 方法來獲取動畫持續時間。
3. UIKit調用適當的方法來啟動動畫:
* 對于非交互式動畫,UIKit 調用動畫器對象的 [animateTransition:](https://developer.apple.com/documentation/uikit/uiviewcontrolleranimatedtransitioning/1622061-animatetransition) 方法。
* 對于交互式動畫,UIKit 調用交互式動畫器對象的 [startInteractiveTransition:](https://developer.apple.com/documentation/uikit/uiviewcontrollerinteractivetransitioning/1622028-startinteractivetransition) 方法。
4. UIKit 等待動畫器對象調用 context transitioning 對象的 [completeTransition:](https://developer.apple.com/documentation/uikit/uiviewcontrollercontexttransitioning/1622042-completetransition) 方法。
自定義動畫器對象在動畫完成后調用此方法,通常在動畫的完成回調代碼塊中。 調用這個方法結束轉換,并讓 UIKit 知道它可以調用 [ presentViewController:animated:completion:](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621380-presentviewcontroller) 的完成回調代碼塊,并調用動畫器對象自己的 [animationEnded:](https://developer.apple.com/documentation/uikit/uiviewcontrolleranimatedtransitioning/1622059-animationended) 方法。
關閉視圖控制器時,UIKit 會調用轉換代理的 [animationControllerForDismissedController:](https://developer.apple.com/documentation/uikit/uiviewcontrollertransitioningdelegate/1622047-animationcontrollerfordismissedc) 方法,并執行以下步驟:
1. UIKit 調用轉換委托的 [interactionControllerForDismissal:](https://developer.apple.com/documentation/uikit/uiviewcontrollertransitioningdelegate/1622030-interactioncontrollerfordismissa) 方法來查看交互式動畫對象是否可用。 如果該方法返回 nil ,那么 UIKit 將執行無需用戶交互的動畫。
2. UIKit 調用動畫器對象的 [transitionDuration:](https://developer.apple.com/documentation/uikit/uiviewcontrolleranimatedtransitioning/1622032-transitionduration) 方法來獲取動畫持續時間。
3. UIKit調用適當的方法來啟動動畫:
* 對于非交互式動畫,UIKit 調用動畫器對象的 [animateTransition:](https://developer.apple.com/documentation/uikit/uiviewcontrolleranimatedtransitioning/1622061-animatetransition) 方法。
* 對于交互式動畫,UIKit 調用交互式動畫器對象的 [startInteractiveTransition:](https://developer.apple.com/documentation/uikit/uiviewcontrollerinteractivetransitioning/1622028-startinteractivetransition) 方法。
4. UIKit 等待動畫器對象調用 context transitioning 對象的 [completeTransition:](https://developer.apple.com/documentation/uikit/uiviewcontrollercontexttransitioning/1622042-completetransition) 方法。
自定義動畫器對象在動畫完成后調用此方法,通常在動畫的完成回調代碼塊中。 調用這個方法結束轉換,并讓 UIKit 知道它可以調用 [ presentViewController:animated:completion:](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621380-presentviewcontroller) 的完成回調代碼塊,并調用動畫器對象自己的 [animationEnded:](https://developer.apple.com/documentation/uikit/uiviewcontrolleranimatedtransitioning/1622059-animationended) 方法。
> 重要提示
在動畫結束時必需調用 [completeTransition:](https://developer.apple.com/documentation/uikit/uiviewcontrollercontexttransitioning/1622042-completetransition) 方法。 UIKit 不會結束轉換過程,從而將控制返回到應用程序,直到調用該方法。
#### 1.3 轉換上下文對象
在轉換動畫開始之前,UIKit 創建一個轉換的上下文對象并填充關于如何執行動畫的信息。 轉換上下文對象是您的代碼的重要組成部分。 它實現了 [UIViewControllerContextTransitioning](https://developer.apple.com/documentation/uikit/uiviewcontrollercontexttransitioning) 協議,并存儲轉換中涉及的視圖控制器和視圖的引用。 它還存儲有關如何執行轉換的信息,包括動畫是否是交互式的。 動畫器對象需要所有這些信息來設置和執行實際的動畫。
> 重要提示
設置自定義動畫時,請始終使用轉換上下文對象中的對象和數據,而不要使用自己管理的任何緩存信息。 轉換可能發生在各種條件下,其中一些可能會改變動畫參數。 轉換的上下文對象保證有正確的信息來執行動畫,而你的動畫器的方法被調用的時候,你的緩存信息可能是無效的。
圖 10-2 顯示了轉換上下文對象如何與其他對象進行交互。 動畫器對象在其 [animateTransition:](https://developer.apple.com/documentation/uikit/uiviewcontrolleranimatedtransitioning/1622061-animatetransition) 方法中接收該對象。 創建的動畫應該在提供的容器視圖中進行。 例如,在呈現視圖控制器時,將其視圖添加為容器視圖的子視圖。 容器視圖可能是窗口或常規視圖,但始終配置為運行您的動畫。
###### 圖 10-2 轉換上下文對象

有關轉換上下文對象的更多信息,請參見 [UIViewControllerContextTransitioning Protocol Reference](https://developer.apple.com/documentation/uikit/uiviewcontrollercontexttransitioning) 。
#### 1.4 轉換協調器
對于內置轉換和自定義轉換,UIKit 都會創建一個轉換協調器對象,以方便可能需要執行的任何額外動畫。 除了呈現和關閉視圖控制器,當發生界面旋轉時或視圖控制器的大小位置更改時,可能會發生轉換。 所有這些轉換都代表視圖層次結構的變化。轉換協調器是一種跟蹤這些變化并同時動畫自己的內容的方式。 要訪問轉換協調器,請獲取受影響的視圖控制器的 [transitionCoordinator](https://developer.apple.com/documentation/uikit/uiviewcontroller/1619294-transitioncoordinator) 屬性中的對象。 轉換協調器僅在轉換期間存在。
圖 10-3 顯示了轉換協調器與轉換中涉及的視圖控制器之間的關系。使用過渡協調器獲取有關轉換的信息,并注冊要與轉換動畫同時執行的動畫代碼塊。轉換協調器對象遵守 UIViewControllerTransitionCoordinatorContext 協議,該協議提供時序信息,動畫當前狀態的有關信息以及轉換中涉及的視圖和視圖控制器。當動畫塊被執行時,它們都會收到一個具有相同信息的上下文對象。
###### 圖 10-3 轉換協調器對象

有關轉換協調器對象的更多信息,請參閱 [UIViewControllerTransitionCoordinator Protocol Reference](https://developer.apple.com/documentation/uikit/uiviewcontrollertransitioncoordinator) 。 有關可用于配置動畫的上下文信息的信息,請參閱 [UIViewControllerTransitionCoordinatorContext Protocol Reference](https://developer.apple.com/documentation/uikit/uiviewcontrollertransitioncoordinatorcontext) 。
### 第二節 使用自定義動畫呈現視圖控制器
要使用自定義動畫呈現視圖控制器,請在現有視圖控制器的操作方法中執行以下操作:
1. 創建想要呈現的視圖控制器
2. 創建自定義轉換代理對象,并將其分配給視圖控制器的 [transitioningDelegate](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621421-transitioningdelegate) 屬性。 轉換代理的方法應該在被調用時創建并返回自定義的動畫器對象。
3. 調用 [presentViewController:animated:completion:](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621380-presentviewcontroller) 方法來呈現視圖控制器。
當調用 [presentViewController:animated:completion:](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621380-presentviewcontroller) 方法時,UIKit 將啟動轉換過程。 轉換在下一次運行循環迭代期間開始并繼續,直到自定義動畫器調用 [completeTransition:](https://developer.apple.com/documentation/uikit/uiviewcontrollercontexttransitioning/1622042-completetransition) 方法。 交互式轉換允許在轉換過程中處理觸摸事件,但非交互式轉換會在動畫器對象指定的持續時間內運行。
### 第三節 實現轉換代理
轉換代理的目的是創建并返回自定義對象。 清單 10-1 顯示了轉換方法的實現是多么的簡單。 這個例子創建并返回一個自定義的動畫器對象。 實際上大部分的工作都是由動畫器對象本身來處理的。
###### 清單 10-1 創建一個動畫器對象
~~~
- (id<UIViewControllerAnimatedTransitioning>)
animationControllerForPresentedController:(UIViewController *)presented
presentingController:(UIViewController *)presenting
sourceController:(UIViewController *)source {
MyAnimator* animator = [[MyAnimator alloc] init];
return animator;
}
~~~
轉換代理的其他方法可以像前面的清單一樣簡單。還可以自定義邏輯來基于應用程序的當前狀態返回不同的動畫器對象。 有關轉換委托方法的更多信息,請參閱 [UIViewControllerTransitioningDelegate Protocol Reference](https://developer.apple.com/documentation/uikit/uiviewcontrollertransitioningdelegate) 。
### 第四節 實現動畫器對象
動畫器對象是遵守 [UIViewControllerAnimatedTransitioning](https://developer.apple.com/documentation/uikit/uiviewcontrolleranimatedtransitioning) 協議的任何對象。動畫器對象創建在固定時間內執行的動畫。動畫器對象的關鍵是它的 [animateTransition:](https://developer.apple.com/documentation/uikit/uiviewcontrolleranimatedtransitioning/1622061-animatetransition) 方法,用于創建實際的動畫。動畫的過程大致分為以下幾個部分:
1. 獲取動畫參數
2. 使用 Core Animation 或 UIView Animation 方法創建動畫
3. 清理并完成轉換
#### 4.1 獲取動畫參數
傳遞給 [animateTransition:](https://developer.apple.com/documentation/uikit/uiviewcontrolleranimatedtransitioning/1622061-animatetransition) 方法的上下文轉換對象包含執行動畫時要使用的數據。從上下文轉換對象獲取更多最新信息時,切勿使用自己的緩存信息或從視圖控制器獲取信息。呈現和關閉視圖控制器有時會涉及視圖控制器之外的對象。例如,自定義的轉換控制器可能會為轉換添加一個背景視圖。 上下文轉換對象會考慮額外的視圖和對象,并為您提供正確的動畫視圖。
* 調用 [viewControllerForKey:](https://developer.apple.com/documentation/uikit/uiviewcontrollercontexttransitioning/1622043-viewcontroller) 方法兩次,以獲得轉換中涉及的“from”和“to”視圖控制器。永遠不要假設你知道哪些視圖控制器參與了轉換。UIKit 可能會改變視圖控制器以便適應新的特性環境或響應應用程序的請求。
* 調用 [containerView](https://developer.apple.com/documentation/uikit/uiviewcontrollercontexttransitioning/1622045-containerview) 方法來獲取動畫的父視圖。 將所有關鍵子視圖添加到此視圖。 例如,在轉換期間,將被呈現的視圖控制器的視圖添加到此視圖。
* 調用 [viewForKey:](https://developer.apple.com/documentation/uikit/uiviewcontrollercontexttransitioning/1622055-viewforkey) 方法來獲取被添加或刪除的視圖。視圖控制器的視圖可能不是唯一在轉換期間添加或刪除的視圖。轉換控制器可以將視圖插入到必須添加或刪除的層次結構中。 [viewForKey:](https://developer.apple.com/documentation/uikit/uiviewcontrollercontexttransitioning/1622055-viewforkey) 方法返回包含你需要添加或刪除的所有內容的根視圖。
* 調用 [finalFrameForViewController:](https://developer.apple.com/documentation/uikit/uiviewcontrollercontexttransitioning/1622024-finalframeforviewcontroller) 方法來獲取被添加或刪除視圖的最終大小位置。
上下文轉換對象使用“from”和“to”命名,以識別轉換過程中涉及的視圖控制器、視圖和 frame 。“from”視圖控制器始終是在轉換開始時屏幕上顯示的視圖控制器,而“to”視圖控制器是在轉換結束時可以看到的視圖控制器。正如在圖 10-4 中所看到的,“from”和“to”視圖控制器在顯示和被關閉之間交換位置。
###### 圖 10-4 from 和 to對象

交換這些值可以更容易地編寫一個處理顯示和關閉的動畫器。 當設計動畫器時,所要做的就是包含一個屬性來知道它是顯示動畫還是關閉動畫。 兩者之間唯一需要的區別如下:
* 對于顯示,請將“to”視圖添加到容器視圖層次結構中。
* 對于關閉,從容器視圖層次結構中刪除“from”視圖。
#### 4.2 創建轉換動畫
在一個典型的轉換中,屬于被呈現的視圖控制器的視圖會被以動畫移動到目標位置。其他視圖可以作為轉換的一部分進行動畫,但是動畫的主要目標是將視圖添加到視圖層次結構中。
動畫主視圖時,您配置動畫的基本操作是相同的。 您可以從轉換的上下文對象中獲取所需的對象和數據,并使用該信息創建實際的動畫。
* 顯示動畫:
* 使用 [viewControllerForKey:](https://developer.apple.com/documentation/uikit/uiviewcontrollercontexttransitioning/1622043-viewcontroller) 和 [viewForKey:](https://developer.apple.com/documentation/uikit/uiviewcontrollercontexttransitioning/1622055-viewforkey) 方法來檢索轉換中涉及的視圖控制器和視圖。
* 設置“to”視圖的起始位置。 將其他任何屬性也設置為它們的起始值。
* 從轉換上下文的 [finalFrameForViewController:](https://developer.apple.com/documentation/uikit/uiviewcontrollercontexttransitioning/1622024-finalframeforviewcontroller) 方法獲取“to”視圖的結束位置。
* 將“to”視圖添加為容器視圖的子視圖。
* 創建動畫。
* 在動畫代碼塊中,將“to”視圖移動到容器視圖的最終位置。將任何其他屬性設置為它們的最終值。
* 在完成代碼塊中,調用 [completeTransition:](https://developer.apple.com/documentation/uikit/uiviewcontrollercontexttransitioning/1622042-completetransition) 方法,并執行清理工作。
* 關閉動畫:
* 使用 [viewControllerForKey:](https://developer.apple.com/documentation/uikit/uiviewcontrollercontexttransitioning/1622043-viewcontroller) 和 [viewForKey:](https://developer.apple.com/documentation/uikit/uiviewcontrollercontexttransitioning/1622055-viewforkey) 方法來檢索轉換中涉及的視圖控制器和視圖。
* 計算“from”視圖的結束位置。這個視圖屬于已經被解散的視圖控制器。
* 將“to”視圖添加為容器視圖的子視圖。
在顯示期間,當轉換完成時,屬于呈現視圖控制器的視圖將被刪除。因此,在一次關閉操作中,必須將該視圖添加到容器中。
* 創建動畫。
* 在動畫代碼塊中,將“from”視圖移動到容器視圖的最終位置。將任何其他屬性設置為它們的最終值。
* 在完成代碼塊中,從視圖層次中刪除“from”視圖,調用 [completeTransition:](https://developer.apple.com/documentation/uikit/uiviewcontrollercontexttransitioning/1622042-completetransition) 方法,并根據需要執行清理工作。
圖 10-5 顯示了一個自定義的顯示和關閉轉換,可以對角地呈現視圖。 在轉換過程中,呈現的視圖從屏幕外開始,向左上角方向移動,直到可見。 在關閉的過程中,視圖反轉了方向,向右下角移動,直到再次離屏。
###### 圖 10-5 自定義顯示和關閉

清單 10-2 展示了如何實現圖 10-5 中所示的轉換。在獲取動畫所需的對象之后,[animateTransition:](https://developer.apple.com/documentation/uikit/uiviewcontrolleranimatedtransitioning/1622061-animatetransition) 方法計算受影響視圖的 frame 。在顯示過程中,被呈現的視圖由 `toView` 變量表示。在關閉過程中,被關閉的視圖由 `fromView` 變量表示。`presenting`屬性是動畫器對象本身的自定義屬性,當創建動畫器時,轉換代理設置為適當的值。
###### 清單 10-2 實現對角顯示和關閉的動畫
~~~
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext {
// 獲取相關對象 (Get the set of relevant objects)
UIView *containerView = [transitionContext containerView];
UIViewController *fromVC = [transitionContext
viewControllerForKey:UITransitionContextFromViewControllerKey];
UIViewController *toVC = [transitionContext
viewControllerForKey:UITransitionContextToViewControllerKey];
UIView *toView = [transitionContext viewForKey:UITransitionContextToViewKey];
UIView *fromView = [transitionContext viewForKey:UITransitionContextFromViewKey];
// 設置動畫所需變量 (Set up some variables for the animation)
CGRect containerFrame = containerView.frame;
CGRect toViewStartFrame = [transitionContext initialFrameForViewController:toVC];
CGRect toViewFinalFrame = [transitionContext finalFrameForViewController:toVC];
CGRect fromViewFinalFrame = [transitionContext finalFrameForViewController:fromVC];
// 設置動畫參數 (Set up the animation parameters)
if (self.presenting) {
// 修改被呈現的視圖的 frame,使其從屏幕右下角的容器開始(Modify the frame of the presented view so that it starts offscreen at the lower-right corner of the container)
toViewStartFrame.origin.x = containerFrame.size.width;
toViewStartFrame.origin.y = containerFrame.size.height;
}
else {
// 修改關閉視圖的 frame,使其在容器視圖的右下角結束(Modify the frame of the dismissed view so it ends in the lower-right corner of the container view.)
fromViewFinalFrame = CGRectMake(containerFrame.size.width,
containerFrame.size.height,
toView.frame.size.width,
toView.frame.size.height);
}
// 總是將“to”視圖添加到容器中 (Always add the "to" view to the container)
// 設置它的起始幀 (And it doesn't hurt to set its start frame)
[containerView addSubview:toView];
toView.frame = toViewStartFrame;
// 使用動畫器返回動畫時間 (Animate using the animator's own duration value)
[UIView animateWithDuration:[self transitionDuration:transitionContext]
animations:^{
if (self.presenting) {
// 移動被顯示視圖到最終位置 (Move the presented view into position)
[toView setFrame:toViewFinalFrame];
}
else {
// 被關閉視圖移除屏幕 (Move the dismissed view offscreen)
[fromView setFrame:fromViewFinalFrame];
}
}
completion:^(BOOL finished){
BOOL success = ![transitionContext transitionWasCancelled];
// 顯示失敗或者關閉之后移除視圖 (After a failed presentation or successful dismissal, remove the view)
if ((self.presenting && !success) || (!self.presenting && success)) {
[toView removeFromSuperview];
}
//通知 UIKit 轉換結束 (Notify UIKit that the transition has finished)
[transitionContext completeTransition:success];
}];
}
~~~
#### 4.3 動畫完成后進行清理
在轉換動畫結束時,調用 [completeTransition:](https://developer.apple.com/documentation/uikit/uiviewcontrollercontexttransitioning/1622042-completetransition) 方法至關重要。 調用這個方法會告訴 UIKit 轉換已經完成,用戶可能會開始使用被呈現的視圖控制器。調用該方法也引發一連串的其他完成回調方法,包括一個來自 [presentViewController:animated:completion:](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621380-presentviewcontroller) 方法和動畫器對象的 [animationEnded: ](https://developer.apple.com/documentation/uikit/uiviewcontrolleranimatedtransitioning/1622059-animationended)方法。調用 [completeTransition:](https://developer.apple.com/documentation/uikit/uiviewcontrollercontexttransitioning/1622042-completetransition) 方法最佳位置是在動畫代碼塊的完成回調方法中。
因為轉換可以被取消,所以應該使用上下文對象的 [transitionWasCancelled](https://developer.apple.com/documentation/uikit/uiviewcontrollercontexttransitioning/1622039-transitionwascancelled) 方法的返回值來確定需要進行哪些清理工作。當一個顯示被取消時,動畫器必須撤銷它對視圖層次結構所做的任何修改。成功的關閉需要采取類似的行動。
### 第五節 為轉換添加交互性
使用交互動畫最簡單的方法是使用 [UIPercentDrivenInteractiveTransition](https://developer.apple.com/documentation/uikit/uipercentdriveninteractivetransition) 對象。`UIPercentDrivenInteractiveTransition` 對象與現有的動畫器對象一起工作來控制動畫的時間。 使用提供的完成進度百分比值執行此操作。 所要做的就是設置事件處理代碼,以計算完成百分比值,并在每個新事件到達時更新它。
可以使用 `UIPercentDrivenInteractiveTransition` 類或其子類。如果是子類,則使用子類的 `init` 方法(或 `startInteractiveTransition:` 方法)執行事件處理代碼的一次性設置。之后,使用自定義的事件處理代碼來計算新的完成百分比值,并調用 `updateInteractiveTransition:` 方法。當你的代碼確定轉換完成時,調用 `finishInteractiveTransition` 方法。
清單 10-3 顯示了 `UIPercentDrivenInteractiveTransition` 子類的 `startInteractiveTransition:`方法的自定義實現。此方法設置了一個拖動手勢識別器來跟蹤觸摸事件,并在動畫的容器視圖上安裝該手勢識別器。它還保存了稍后使用的轉換上下文的引用。
###### 清單 10-3 配置一個百分比驅動的交互式動畫
~~~
- (void)startInteractiveTransition:(id<UIViewControllerContextTransitioning>)transitionContext {
// 調用父類初始化方法(Always call super first)
[super startInteractiveTransition:transitionContext];
// 保存轉換上下文引用(Save the transition context for future reference)
self.contextData = transitionContext;
// 創建拖動手勢跟在觸摸事件(Create a pan gesture recognizer to monitor events)
self.panGesture = [[UIPanGestureRecognizer alloc]
initWithTarget:self action:@selector(handleSwipeUpdate:)];
self.panGesture.maximumNumberOfTouches = 1;
//容器視圖上安裝該手勢識別器( Add the gesture recognizer to the container view)
UIView* container = [transitionContext containerView];
[container addGestureRecognizer:self.panGesture];
}
~~~
手勢識別器為每個到達的新事件調用其操作方法。 操作方法的實現可以使用手勢識別器的狀態信息來確定手勢是成功、失敗還是仍在進行中。 同時,可以使用最新的觸摸事件信息來計算手勢的新百分比值。
清單 10-4 顯示了清單 10-3 中配置的拖動手勢識別器所調用的方法。 當新事件到達時,此方法使用垂直方向上的移動距離來計算動畫的完成百分比。 當手勢結束時,該方法結束轉換。
###### 清單 10-4 使用事件來更新動畫進度
~~~
-(void)handleSwipeUpdate:(UIGestureRecognizer *)gestureRecognizer {
UIView* container = [self.contextData containerView];
if (gestureRecognizer.state == UIGestureRecognizerStateBegan) {
// 重置手勢開始時的進度值(Reset the translation value at the beginning of the gesture)
[self.panGesture setTranslation:CGPointMake(0, 0) inView:container];
}
else if (gestureRecognizer.state == UIGestureRecognizerStateChanged) {
// 獲取當前進度值(Get the current translation value)
CGPoint translation = [self.panGesture translationInView:container];
// 計算手勢相對于容器視圖的高度垂直移動的距離(Compute how far the gesture has travelled vertically relative to the height of the container view.)
CGFloat percentage = fabs(translation.y / CGRectGetHeight(container.bounds));
// 使用百分比進度值更新動畫器 (Use the translation value to update the interactive animator)
[self updateInteractiveTransition:percentage];
}
else if (gestureRecognizer.state >= UIGestureRecognizerStateEnded) {
// 結束轉換,移除手勢 (Finish the transition and remove the gesture recognizer)
[self finishInteractiveTransition];
[[self.contextData containerView] removeGestureRecognizer:self.panGesture];
}
}
~~~
> 注意
計算的值代表整個動畫長度的完成百分比。 對于交互式動畫,可能需要避免非線性效應,例如動畫本身的初始速度,阻尼值和非線性完成曲線。 這樣的效果往往會將事件的觸摸位置與底層視圖的移動分離。
### 第六節 創建與轉換同時運行的動畫
參與轉換的視圖控制器可以在任何顯示或轉換動畫的基礎上執行其他動畫。例如,被呈現的視圖控制器可以在轉換期間對其自己的視圖層次結構進行動畫處理,并在轉換發生時添加運動效果或其他視覺反饋。任何對象都可以創建動畫,只要它能夠訪問被呈現或呈現視圖控制器的 [transitionCoordinator](https://developer.apple.com/documentation/uikit/uiviewcontroller/1619294-transitioncoordinator) 屬性即可。轉換協調員器只有在正在進行轉換時才存在。
請調用轉換協調器的 [animateAlongsideTransition:completion:](https://developer.apple.com/documentation/uikit/uiviewcontrollertransitioncoordinator/1619300-animate) 或 [animateAlongsideTransitionInView:animation:completion:](https://developer.apple.com/documentation/uikit/uiviewcontrollertransitioncoordinator/1619295-animatealongsidetransitioninview) 方法來創建動畫。提供的代碼塊將被存儲,直到轉換動畫開始,此時它們將與其余的轉換動畫一起執行。
### 第七節 使用呈現控制器處理動畫
對于自定義轉換,可以提供自己的轉換控制器,使被呈現視圖控制器具有自定義外觀。轉換控制器管理與視圖控制器及其內容分離的任何自定義鑲邊(過渡效果)。例如,放置在視圖控制器視圖后面的蒙版視圖將由轉換控制器管理。它沒有管理特定視圖控制器的視圖,這意味著可以在的應用中使用相同的轉換控制器。
可以從被呈現視圖控制的轉換代理提供自定義轉換控制器。(視圖控制器的 [modalTransitionStyle](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621388-modaltransitionstyle) 屬性必須是 [UIModalPresentationCustom](https://developer.apple.com/documentation/uikit/uimodalpresentationstyle/1621375-custom) 。)轉換控制器與動畫器對象并行運行。由于動畫器將對視圖控制器的視圖進行動畫處理,所以表示控制器可將任何其他視圖動畫移動到目標位置。在轉換結束時,轉換控制器有機會對視圖層次結構執行最終的調整。
有關如何創建自定義轉換控制器的信息,請參閱 [Creating Custom Presentations](https://developer.apple.com/library/content/featuredarticles/ViewControllerPGforiPhoneOS/DefiningCustomPresentations.html#//apple_ref/doc/uid/TP40007457-CH25-SW1) 。