過(guò)渡動(dòng)畫(huà)提供應(yīng)用界面改變的視覺(jué)反饋。UIKit提供一組標(biāo)準(zhǔn)過(guò)渡樣式,用于present視圖控制器時(shí)使用,你可以自定義過(guò)渡補(bǔ)充標(biāo)準(zhǔn)過(guò)渡。
過(guò)渡動(dòng)畫(huà)序列
過(guò)渡動(dòng)畫(huà)互換視圖控制器的內(nèi)容。有兩種類(lèi)型的過(guò)渡:present和dismiss。present過(guò)渡會(huì)在應(yīng)用視圖層級(jí)結(jié)構(gòu)中添加一個(gè)新的視圖控制器,而dismiss過(guò)渡會(huì)從層級(jí)結(jié)構(gòu)中刪除一個(gè)或多個(gè)視圖控制器。
過(guò)渡動(dòng)畫(huà)的實(shí)現(xiàn)需要很多對(duì)象。UIKit提供所有涉及對(duì)象的默認(rèn)版本,你可以自定義這些對(duì)象或僅僅繼承它們。如果你選擇正確的對(duì)象,你可以使用少量的代碼來(lái)創(chuàng)建動(dòng)畫(huà)。如果你利用UIKit提供的現(xiàn)成代碼,可以很容易實(shí)現(xiàn)交互式動(dòng)畫(huà)。
過(guò)渡委托
過(guò)渡動(dòng)畫(huà)和自定義present的起點(diǎn)是過(guò)渡委托。過(guò)渡委托是你定義的一個(gè)對(duì)象,符合 UIViewControllerTransitioningDelegate協(xié)議。它的工作是為UIKit提供以下對(duì)象:
- 動(dòng)畫(huà)對(duì)象,一個(gè)動(dòng)畫(huà)對(duì)象負(fù)責(zé)創(chuàng)建動(dòng)畫(huà),用于顯示或隱藏視圖控制器的視圖。過(guò)渡委托可以提供獨(dú)立的動(dòng)畫(huà)對(duì)象,用來(lái)present和dismiss視圖控制器。動(dòng)畫(huà)對(duì)象符合 UIViewControllerAnimatedTransitioning協(xié)議。
- 交互式動(dòng)畫(huà)對(duì)象。交互式動(dòng)畫(huà)對(duì)象使用觸摸事件或手勢(shì)識(shí)別器,驅(qū)動(dòng)自定義動(dòng)畫(huà)時(shí)間。交互式動(dòng)畫(huà)對(duì)象符合UIViewControllerInteractiveTransitioning協(xié)議。
創(chuàng)建一個(gè)交互式動(dòng)畫(huà)最簡(jiǎn)單的方法是繼承 UIPercentDrivenInteractiveTransition 類(lèi),并往子類(lèi)中添加事件處理代碼。該類(lèi)使用現(xiàn)有的動(dòng)畫(huà)對(duì)象來(lái)控制創(chuàng)建動(dòng)畫(huà)的時(shí)間。如果你創(chuàng)建自己的交互式動(dòng)畫(huà),你必須自己渲染動(dòng)畫(huà)的每一幀。
- present控制器。present控制器管理屏幕上視圖控制器的present風(fēng)格。系統(tǒng)提供了內(nèi)置的present風(fēng)格,你可以給自定義present控制器設(shè)置你自己的present風(fēng)格。關(guān)于創(chuàng)建自定義present控制器的更多信息,參見(jiàn)創(chuàng)建自定義present(Creating Custom Presentations)。
分配過(guò)渡代理到視圖控制的 transitioningDelegate屬性,告訴UIKit你希望執(zhí)行一個(gè)自定義的過(guò)渡或present。你的代理可以選擇哪個(gè)對(duì)象。如果你不提供動(dòng)畫(huà)對(duì)象,UIKit使用視圖控制器的modalTransitionStyle屬性中的標(biāo)準(zhǔn)過(guò)渡動(dòng)畫(huà)。
圖10-1展示了過(guò)渡代理、presented視圖控制器動(dòng)畫(huà)對(duì)象之間的關(guān)系。只有當(dāng)視圖控制器的 modalPresentationStyle屬性被設(shè)置為UIModalPresentationCustom,才使用present控制器。

關(guān)于如何實(shí)現(xiàn)過(guò)渡代理的更多信息,參見(jiàn)過(guò)渡代理的實(shí)現(xiàn)( Implementing the Transitioning Delegate)。關(guān)于過(guò)渡代理對(duì)象的方法的更多信息,參見(jiàn)UIViewControllerTransitioningDelegate協(xié)議引用(UIViewControllerTransitioningDelegate Protocol Reference)。
自定義動(dòng)畫(huà)序列
當(dāng)presented視圖控制器的 transitioningDelegate屬性包含一個(gè)有效的對(duì)象,UIKit使用你提供的自定義動(dòng)畫(huà)對(duì)象present視圖控制器。當(dāng)它準(zhǔn)備present,UIKit調(diào)用過(guò)渡代理的animationControllerForPresentedController:presentingController:sourceController: 方法,檢索自定義動(dòng)畫(huà)對(duì)象。如果對(duì)象可用,UIKit執(zhí)行以下步驟:
- UIKit調(diào)用過(guò)渡代理的 interactionControllerForPresentation:方法,查看是否有可用的交互動(dòng)畫(huà)對(duì)象。如果該方法返回nil,UIKit執(zhí)行沒(méi)有交互的動(dòng)畫(huà)。
- UIKit調(diào)用動(dòng)畫(huà)對(duì)象的 transitionDuration: 方法來(lái)獲取動(dòng)畫(huà)持續(xù)時(shí)間。
- UIKit調(diào)用適當(dāng)?shù)姆椒▎?dòng)動(dòng)畫(huà):對(duì)于非交互式動(dòng)畫(huà),UIKit調(diào)用動(dòng)畫(huà)對(duì)象的 animateTransition:方法。對(duì)于交互式動(dòng)畫(huà),UIKit調(diào)用交互式動(dòng)畫(huà)對(duì)象的 startInteractiveTransition:方法。
- UIKit等待動(dòng)畫(huà)對(duì)象調(diào)用環(huán)境過(guò)渡對(duì)象的 completeTransition:方法。
在動(dòng)畫(huà)完成后,自定義動(dòng)畫(huà)調(diào)用該方法,一般在動(dòng)畫(huà)的完成block中。調(diào)用該方法會(huì)結(jié)束過(guò)渡,讓UIKit知道可以調(diào)用完成處理器的 presentViewController:animated:completion:方法,調(diào)用動(dòng)畫(huà)對(duì)象自己的animationEnded:方法。
當(dāng)dismiss一個(gè)視圖控制器,UIKit調(diào)用過(guò)渡代理的 animationControllerForDismissedController:方法并執(zhí)行以下步驟:
- UIKit調(diào)用過(guò)渡代理的 interactionControllerForDismissal:方法,查看是否有可用的交互式動(dòng)畫(huà)對(duì)象。如果該方法返回nil,UIKit執(zhí)行沒(méi)有交互的動(dòng)畫(huà)。
- UIKit調(diào)用動(dòng)畫(huà)對(duì)象的 transitionDuration: 方法來(lái)獲取動(dòng)畫(huà)持續(xù)時(shí)間。
- UIKit調(diào)用適當(dāng)?shù)姆椒▎?dòng)動(dòng)畫(huà):對(duì)于非交互式動(dòng)畫(huà),UIKit調(diào)用動(dòng)畫(huà)對(duì)象的 animateTransition:方法。對(duì)于交互式動(dòng)畫(huà),UIKit調(diào)用交互式動(dòng)畫(huà)對(duì)象的 startInteractiveTransition:方法。
- UIKit等待動(dòng)畫(huà)對(duì)象調(diào)用環(huán)境過(guò)渡對(duì)象的 completeTransition:方法。
在動(dòng)畫(huà)完成后,自定義動(dòng)畫(huà)調(diào)用該方法,一般在動(dòng)畫(huà)的完成block中。調(diào)用該方法會(huì)結(jié)束過(guò)渡,讓UIKit知道可以調(diào)用完成處理器的 presentViewController:animated:completion:方法,調(diào)用動(dòng)畫(huà)對(duì)象自己的animationEnded:方法。
重要:必須在動(dòng)畫(huà)結(jié)束時(shí)調(diào)用 completeTransition: 方法。直到調(diào)用該方法,UIKit才結(jié)束過(guò)渡過(guò)程,從而返回到應(yīng)用。
過(guò)渡環(huán)境對(duì)象
過(guò)渡動(dòng)畫(huà)開(kāi)始前,UIKit創(chuàng)建過(guò)渡環(huán)境對(duì)象,并添加如何執(zhí)行動(dòng)畫(huà)的信息。過(guò)渡環(huán)境對(duì)象是代碼中的重要部分。它實(shí)現(xiàn)了UIViewControllerContextTransitioning協(xié)議并存儲(chǔ)與過(guò)渡相關(guān)的視圖控制器和視圖的引用。它還存儲(chǔ)如何執(zhí)行過(guò)渡的信息,包括該動(dòng)畫(huà)是否為交互式。動(dòng)畫(huà)對(duì)象需要這些信息來(lái)建立和執(zhí)行實(shí)際動(dòng)畫(huà)。
重要:當(dāng)設(shè)置自定義動(dòng)畫(huà)時(shí),總是使用過(guò)渡環(huán)境對(duì)象中的對(duì)象和數(shù)據(jù),而不是你自己管理的任何緩存信息??梢栽谌魏螚l件下過(guò)渡,其中一些情況會(huì)改變動(dòng)畫(huà)參數(shù)。保護(hù)過(guò)渡環(huán)境對(duì)象,這樣可以保證執(zhí)行動(dòng)畫(huà)的正確信息,而你的緩存信息可能是調(diào)用動(dòng)畫(huà)方法時(shí)產(chǎn)生的過(guò)期信息。
圖10-2展示了過(guò)渡環(huán)境對(duì)象如何與其他對(duì)象交互。動(dòng)畫(huà)對(duì)象接收其 animateTransition:方法的對(duì)象。你創(chuàng)建的動(dòng)畫(huà)應(yīng)該發(fā)生在提供的容器視圖中。例如,當(dāng)present一個(gè)視圖控制器,將其視圖添加到容器視圖中作為子視圖。容器視圖可能是窗口或一個(gè)普通視圖,但是配置容器視圖是為了運(yùn)行動(dòng)畫(huà)。

關(guān)于過(guò)渡環(huán)境對(duì)象的更多信息,參見(jiàn)UIViewControllerContextTransitioning 協(xié)議引用(UIViewControllerContextTransitioning Protocol Reference)。
過(guò)渡協(xié)調(diào)器
內(nèi)置過(guò)渡和自定義過(guò)渡,UIKit創(chuàng)建過(guò)渡協(xié)調(diào)器對(duì)象幫助需要執(zhí)行的額外動(dòng)畫(huà)。除了視圖控制器present和dismiss,當(dāng)界面發(fā)生旋轉(zhuǎn)或視圖控制器的frame改變時(shí)會(huì)發(fā)生過(guò)渡。所有這些過(guò)渡代表視圖層級(jí)有變化。過(guò)渡協(xié)調(diào)器總是跟蹤這些變化同時(shí)渲染內(nèi)容。訪問(wèn)過(guò)渡協(xié)調(diào)器,獲取受影響的視圖控制器的 transitionCoordinator 屬性的對(duì)象。過(guò)渡協(xié)調(diào)器只存在過(guò)渡過(guò)程中。
圖10-3展示了present中視圖控制器與過(guò)渡協(xié)調(diào)器的關(guān)系。使用過(guò)渡協(xié)調(diào)器獲取過(guò)渡信息、注冊(cè)動(dòng)畫(huà)block。過(guò)渡協(xié)調(diào)器對(duì)象符合 UIViewControllerTransitionCoordinatorContext協(xié)議,該對(duì)象提供時(shí)間信息、動(dòng)畫(huà)當(dāng)前狀態(tài)信息和過(guò)渡相關(guān)的視圖和視圖控制器。當(dāng)執(zhí)行動(dòng)畫(huà)block,他們接收到具有相同信息的環(huán)境對(duì)象。

關(guān)于過(guò)渡協(xié)調(diào)器對(duì)象的更多信息,參見(jiàn)UIViewControllerTransitionCoordinator 協(xié)議引用(UIViewControllerTransitionCoordinator Protocol Reference)。環(huán)境信息可以用來(lái)配置動(dòng)畫(huà),關(guān)于環(huán)境信息,參見(jiàn)UIViewControllerTransitionCoordinatorContext協(xié)議引用(UIViewControllerTransitionCoordinatorContext Protocol Reference)。
使用自定義動(dòng)畫(huà)present視圖控制器
使用自定義動(dòng)畫(huà)present視圖控制器,在現(xiàn)有視圖控制器中執(zhí)行以下動(dòng)作方法:
- 創(chuàng)建想要present的視圖控制器。
- 創(chuàng)建自定義過(guò)渡代理對(duì)象,并將其分配給視圖控制器的 transitioningDelegate屬性。必須創(chuàng)建過(guò)渡代理的方法,在調(diào)用時(shí)返回自定義動(dòng)畫(huà)對(duì)象。
- 調(diào)用 presentViewController:animated:completion:方法present視圖控制器。
當(dāng)調(diào)用presentViewController:animated:completion:方法,UIKit啟動(dòng)present過(guò)程。在下一個(gè)run loop中啟動(dòng)present,并持續(xù)到自定義動(dòng)畫(huà)調(diào)用 completeTransition: 方法。交互式過(guò)渡允許你在過(guò)渡進(jìn)行中處理觸摸事件,但非交互式過(guò)渡期間運(yùn)行指定動(dòng)畫(huà)對(duì)象。
實(shí)現(xiàn)過(guò)渡代理
過(guò)渡代理的目的是創(chuàng)建并返回自定義對(duì)象。列表10-1中展示了簡(jiǎn)單過(guò)渡方法的實(shí)現(xiàn)。本例創(chuàng)建并返回一個(gè)自定義動(dòng)畫(huà)對(duì)象,大部分實(shí)際工作由動(dòng)畫(huà)對(duì)象本身處理。
列表10-1 創(chuàng)建動(dòng)畫(huà)對(duì)象
<pre><code>
-(id<UIViewControllerAnimatedTransitioning>)
animationControllerForPresentedController:(UIViewController *)presented
presentingController:(UIViewController *)presenting
sourceController:(UIViewController *)source {
MyAnimator* animator = [[MyAnimator alloc] init];
return animator;
}
</pre></code>
過(guò)渡代理的其他方法和前面的方法一樣簡(jiǎn)單。你可以基于應(yīng)用的當(dāng)前狀態(tài),自定義邏輯返回不同動(dòng)畫(huà)對(duì)象。關(guān)于過(guò)渡代理方法的更多信息,參見(jiàn)UIViewControllerTransitioningDelegate 協(xié)議引用(UIViewControllerTransitioningDelegate Protocol Reference)。
實(shí)現(xiàn)動(dòng)畫(huà)對(duì)象
動(dòng)畫(huà)對(duì)象是采用 UIViewControllerAnimatedTransitioning協(xié)議的對(duì)象。動(dòng)畫(huà)對(duì)象創(chuàng)建動(dòng)畫(huà),該動(dòng)畫(huà)在固定時(shí)間執(zhí)行。動(dòng)畫(huà)對(duì)象的關(guān)鍵是 animateTransition:方法,使用該方法可以創(chuàng)建實(shí)際動(dòng)畫(huà)。動(dòng)畫(huà)過(guò)程可以大致分為以下幾個(gè)部分:
- 獲取動(dòng)畫(huà)參數(shù)。
- 使用核心動(dòng)畫(huà)或 UIView 動(dòng)畫(huà)方法創(chuàng)建動(dòng)畫(huà)。
- 清理和完成過(guò)渡
獲取動(dòng)畫(huà)參數(shù)
傳遞給方法的環(huán)境過(guò)渡對(duì)象包含執(zhí)行動(dòng)畫(huà)所需使用的數(shù)據(jù)。當(dāng)你可以從環(huán)境過(guò)渡對(duì)象中獲取最新信息,不要使用緩存信息或者從視圖控制器獲取信息。present和dismiss視圖控制器有時(shí)涉及視圖控制器之外的對(duì)象。例如,自定義present控制器可能會(huì)添加一個(gè)背景視圖作為present的一部分。環(huán)境過(guò)渡對(duì)象考慮額外的視圖,為你提供正確的視圖用于動(dòng)畫(huà)。
- 調(diào)用 viewControllerForKey:方法兩次獲取過(guò)渡中的“from”和“to”視圖控制器。永遠(yuǎn)不要認(rèn)為你知道哪些視圖控制器參與過(guò)渡。在適應(yīng)新環(huán)境或者響應(yīng)app請(qǐng)求時(shí),UIKit可能改變視圖控制器。
- 調(diào)用 containerView方法獲取動(dòng)畫(huà)的父視圖。添加所有關(guān)鍵子視圖到該視圖上。例如,在present期間,添加presented視圖控制器的視圖到該視圖上。
- 調(diào)用 viewForKey:方法獲取添加或刪除的視圖。視圖控制器的視圖可能不是唯一一個(gè)在過(guò)渡期間添加或刪除的視圖。present控制器可能添加視圖到層級(jí)結(jié)構(gòu),這些視圖也必須添加或刪除。 viewForKey:方法返回包含所有你需要添加或刪除的根視圖。
- 調(diào)用 finalFrameForViewController:方法獲取添加或刪除的視圖的最終frame。
環(huán)境過(guò)渡對(duì)象使用“from”和“to”命名法來(lái)識(shí)別視圖控制器、視圖和過(guò)渡中涉及的frame。“from”視圖控制器總是在過(guò)渡前顯示在屏幕上的視圖,“to”視圖控制器是在過(guò)渡之后顯示的視圖。如圖10-4,“from”和“to”視圖控制器在present和dismiss時(shí)交換位置。

交換值使編寫(xiě)一個(gè)同時(shí)處理present和dismiss的動(dòng)畫(huà)變得簡(jiǎn)單。當(dāng)你設(shè)計(jì)動(dòng)畫(huà)時(shí),你需要做的是了解該屬性是渲染present還是dismiss。兩者的唯一區(qū)別如下:
- 對(duì)于present,添加“to”視圖控制器到視圖層級(jí)結(jié)構(gòu)。
- 對(duì)于dimiss,從視圖層級(jí)結(jié)構(gòu)中刪除“from”視圖控制器。
創(chuàng)建過(guò)渡動(dòng)畫(huà)
在典型的present中,屬于presented視圖控制器的視圖會(huì)被渲染。其他視圖可能作為present中的一部分,但動(dòng)畫(huà)的主要目標(biāo)是添加到視圖層級(jí)結(jié)構(gòu)中的視圖。
當(dāng)渲染主要視圖,動(dòng)畫(huà)需要配置的基礎(chǔ)動(dòng)作都是一樣的。從過(guò)渡環(huán)節(jié)對(duì)象中獲取對(duì)象和數(shù)據(jù),使用這些信息創(chuàng)建實(shí)際動(dòng)畫(huà)。
- present動(dòng)畫(huà)使用 viewControllerForKey:和 viewForKey:方法檢索過(guò)渡中涉及的視圖控制器和視圖。設(shè)置“to”視圖的起始位置。設(shè)置其他屬性的初始值。通過(guò)過(guò)渡環(huán)境對(duì)象的 finalFrameForViewController:方法獲取“to”視圖的結(jié)束位置,添加“to”視圖作為容器視圖的子視圖。在動(dòng)畫(huà)block中創(chuàng)建動(dòng)畫(huà),渲染“to”視圖到容器視圖。設(shè)置其他屬性為最終值。在完成block中,調(diào)用 completeTransition:方法執(zhí)行其他清理。
- dismiss動(dòng)畫(huà)使用 viewControllerForKey:和 viewForKey:方法檢索過(guò)渡中涉及的視圖控制器和視圖。計(jì)算“from”視圖的結(jié)束位置。該視圖屬于presented視圖控制器,現(xiàn)在被dismiss。添加“to”視圖到容器視圖作為其子視圖。
在present期間,當(dāng)過(guò)渡完成時(shí),屬于presenting視圖控制器的視圖被刪除。因此,在dismiss過(guò)程中,必須添加該視圖到容器視圖控制器。
- 在動(dòng)畫(huà)block中創(chuàng)建動(dòng)畫(huà),渲染“from”視圖到容器視圖。設(shè)置其他屬性為最終值。在完成block中,調(diào)用completeTransition: 方法從視圖層級(jí)結(jié)構(gòu)中刪除“from”視圖。根據(jù)需要執(zhí)行其他清理。
圖10-5 展示了自定義present和dismiss過(guò)渡,以對(duì)角線的方式渲染。在present期間,presented視圖從屏幕外開(kāi)始以對(duì)角線方式渲染到左邊,直到可見(jiàn)。在dismiss期間,視圖改變其方向,從右下角開(kāi)始消失直到在屏幕外。

列表10-2 展示了如何實(shí)現(xiàn)過(guò)渡,如圖10-5。在檢索到動(dòng)畫(huà)所需的對(duì)象后,animateTransition: 方法計(jì)算受影響的視圖的frame。在present期間,toView變量代表presented視圖。在dismiss期間,fromView變量代表dismiss視圖。presenting屬性是動(dòng)畫(huà)對(duì)象自定義屬性,當(dāng)創(chuàng)建動(dòng)畫(huà)時(shí),過(guò)渡代理設(shè)置一個(gè)適當(dāng)?shù)闹怠?/p>
列表10-2 實(shí)現(xiàn)present和dismiss動(dòng)畫(huà)
<pre><code>
-(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) {
// 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 {
// 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);
}
// 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];
}
// Notify UIKit that the transition has finished
[transitionContext completeTransition:success];
}];
}
</pre></code>
動(dòng)畫(huà)后的清理
在過(guò)渡動(dòng)畫(huà)結(jié)束后,關(guān)鍵要調(diào)用 completeTransition: 方法。調(diào)用該方法告訴UIKit過(guò)渡完成,用戶(hù)可能開(kāi)始使用視圖控制器。調(diào)用該方法也會(huì)觸發(fā)一連串其他完成處理程序,包括 presentViewController:animated:completion:方法和動(dòng)畫(huà)對(duì)象的 animationEnded:方法。最好在動(dòng)畫(huà)block的完成處理程序中調(diào)用completeTransition: 方法。
因?yàn)檫^(guò)渡可以被取消,可以使用環(huán)境對(duì)象的transitionWasCancelled方法返回值來(lái)決定是否需要清理。當(dāng)present被取消,動(dòng)畫(huà)必須撤銷(xiāo)視圖層級(jí)結(jié)構(gòu)的任何修改。一個(gè)成功的dismiss也需要類(lèi)似的動(dòng)作。
在過(guò)渡中添加交互
使動(dòng)畫(huà)可交互的最簡(jiǎn)單的方法是使用 UIPercentDrivenInteractiveTransition 對(duì)象。 UIPercentDrivenInteractiveTransition 對(duì)象使用現(xiàn)有動(dòng)畫(huà)對(duì)象來(lái)控制動(dòng)畫(huà)的時(shí)間。它也使用你提供的完成百分比。所有你需要做的是設(shè)置事件處理代碼,計(jì)算完成百分比和在每個(gè)事件到來(lái)時(shí)更新百分比。
你可以使用UIPercentDrivenInteractiveTransition類(lèi)或繼承該類(lèi)。如果繼承,在子類(lèi)中使用init方法(或startInteractiveTransition:方法)來(lái)執(zhí)行一次性事件處理代碼。之后,使用自定義事件處理代碼來(lái)計(jì)算新完成百分比并調(diào)用updateInteractiveTransition:方法。當(dāng)代碼確定過(guò)渡完成,調(diào)用 finishInteractiveTransition方法。
列表10-3 展示了UIPercentDrivenInteractiveTransition類(lèi)的startInteractiveTransition:方法的自定義實(shí)現(xiàn)。該方法設(shè)置pan手勢(shì)識(shí)別器來(lái)跟蹤觸摸事件,并設(shè)置手勢(shì)識(shí)別器到容器視圖。它也保存過(guò)渡環(huán)境的引用以備后用。
列表10-3 配置percent-driven交互式動(dòng)畫(huà)
<pre><code>
-(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];
}
</pre></code>
手勢(shì)識(shí)別器為每個(gè)到來(lái)的新事件調(diào)用其動(dòng)作方法。動(dòng)作方法的視線可以使用手勢(shì)識(shí)別器的狀態(tài)信息來(lái)確定手勢(shì)是否成功,失敗或仍在進(jìn)行中。在同一時(shí)間,可以使用最新觸摸事件信息來(lái)計(jì)算手勢(shì)的新百分比值。
列表10-4 展示pan手勢(shì)識(shí)別器調(diào)用的方法,如列表10-3所配置。新事件到達(dá),該方法使用垂直距離計(jì)算完成動(dòng)畫(huà)的百分比。當(dāng)手勢(shì)結(jié)束時(shí),該方法完成過(guò)渡。
列表10-4 使用事件更新動(dòng)畫(huà)進(jìn)程
<pre><code>
-(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];
}
}
</pre></code>
注意:計(jì)算的值代表了完成整個(gè)動(dòng)畫(huà)長(zhǎng)度的百分比。對(duì)于交互式動(dòng)畫(huà),你可能希望在動(dòng)畫(huà)中避免非線性效果如初始速度,阻尼值和非線性完成曲線。這些效果往往將事件觸摸位置與底層視圖分割開(kāi)。
創(chuàng)建額外動(dòng)畫(huà)
過(guò)渡中涉及的視圖控制器可以在present或過(guò)渡動(dòng)畫(huà)上執(zhí)行額外的動(dòng)畫(huà)。例如,在過(guò)渡過(guò)程中,presented視圖控制器可能渲染其視圖層級(jí)結(jié)構(gòu),并添加運(yùn)動(dòng)效果或其他視覺(jué)反饋。任何對(duì)象都可以創(chuàng)建動(dòng)畫(huà),只要它能訪問(wèn)presented或presenting視圖控制器的transitionCoordinator屬性。過(guò)渡協(xié)調(diào)器只存在過(guò)渡過(guò)程中。
調(diào)用過(guò)渡協(xié)調(diào)器的 animateAlongsideTransition:completion:和 animateAlongsideTransitionInView:animation:completion: 方法創(chuàng)建動(dòng)畫(huà)。提供的block被存儲(chǔ)直到過(guò)渡動(dòng)畫(huà)開(kāi)始,此時(shí)他們和其他過(guò)渡動(dòng)畫(huà)一起執(zhí)行。
使用present控制器與動(dòng)畫(huà)
對(duì)于自定義present,可以提供自己的present控制器給presented視圖控制器一個(gè)自定義外觀。present控制器管理任何獨(dú)立于視圖控制器和其內(nèi)容的自定義chrome。例如,放置在視圖控制器視圖之后的模糊視圖由present控制器管理。事實(shí)上,它并不管理特定視圖控制器的視圖,這表明在應(yīng)用中的任何視圖控制器可以使用相同的present控制器。
presented視圖控制器的過(guò)渡代理提供一個(gè)自定義present控制器。(視圖控制器的 modalTransitionStyle屬性必須為UIModalPresentationCustom。)present控制器與其他動(dòng)畫(huà)對(duì)象一起運(yùn)行。因?yàn)閯?dòng)畫(huà)對(duì)象渲染視圖控制器的視圖,present控制器渲染其他額外的視圖。在過(guò)渡結(jié)束時(shí),present控制器可以執(zhí)行視圖層級(jí)結(jié)構(gòu)的任何調(diào)整。
關(guān)于如何創(chuàng)建自定義present控制器的信息,參見(jiàn)創(chuàng)建自定義present( Creating Custom Presentations)。
官方原文地址: