過(guò)渡動(dòng)畫(huà)可提供應(yīng)用程序界面變更的視覺(jué)反饋。 UIKit提供了一組標(biāo)準(zhǔn)過(guò)渡樣式,以便在呈現(xiàn)視圖控制器時(shí)使用,并且您可以使用自己的自定義過(guò)渡來(lái)補(bǔ)充標(biāo)準(zhǔn)過(guò)渡。
過(guò)渡動(dòng)畫(huà)
過(guò)渡動(dòng)畫(huà)將一個(gè)視圖控制器的內(nèi)容交換為另一個(gè)的內(nèi)容。 有兩種類(lèi)型的過(guò)渡:呈現(xiàn)和消失。 呈現(xiàn)轉(zhuǎn)換會(huì)向應(yīng)用程序的視圖控制器層次結(jié)構(gòu)添加新的視圖控制器,而取消轉(zhuǎn)換會(huì)從層次結(jié)構(gòu)中刪除一個(gè)或多個(gè)視圖控制器。
實(shí)現(xiàn)過(guò)渡動(dòng)畫(huà)需要許多對(duì)象。 UIKit提供過(guò)渡中涉及的所有對(duì)象的默認(rèn)版本,你可以自定義所有對(duì)象或僅定義一個(gè)子集。 如果你選擇正確的對(duì)象集,你應(yīng)該能夠創(chuàng)建你的動(dòng)畫(huà)只有少量的代碼。 即使包含交互的動(dòng)畫(huà),如果你利用UIKit提供的現(xiàn)有代碼,也可以輕松實(shí)現(xiàn)。
過(guò)渡代理
過(guò)渡代理是過(guò)渡動(dòng)畫(huà)和自定義呈現(xiàn)的起點(diǎn)。 過(guò)渡委托是你定義的并且符合UIViewControllerTransitioningDelegate協(xié)議的對(duì)象。 它的工作是為UIKit提供以下對(duì)象:
- 動(dòng)畫(huà)對(duì)象。 animator對(duì)象負(fù)責(zé)創(chuàng)建用于顯示或隱藏視圖控制器視圖的動(dòng)畫(huà)。 過(guò)渡委托可以提供用于呈現(xiàn)和解除視圖控制器的單獨(dú)的動(dòng)畫(huà)對(duì)象。 Animator對(duì)象符合UIViewControllerAnimatedTransitioning協(xié)議。
- 交互式動(dòng)畫(huà)對(duì)象。 交互式動(dòng)畫(huà)對(duì)象使用觸摸事件或手勢(shì)識(shí)別器來(lái)驅(qū)動(dòng)自定義動(dòng)畫(huà)的定時(shí)。 交互式動(dòng)畫(huà)對(duì)象符合UIViewControllerInteractiveTransitioning協(xié)議。 創(chuàng)建交互式動(dòng)畫(huà)制作最簡(jiǎn)單的方法是將UIPercentDrivenInteractiveTransition類(lèi)子類(lèi)化,并向你的子類(lèi)添加事件處理代碼。 該類(lèi)控制使用現(xiàn)有動(dòng)畫(huà)對(duì)象創(chuàng)建的動(dòng)畫(huà)的時(shí)間。 如果你創(chuàng)建自己的交互式動(dòng)畫(huà)師,您必須自己渲染動(dòng)畫(huà)的每個(gè)幀。
- 呈現(xiàn)控制器。 呈現(xiàn)控制器管理呈現(xiàn)風(fēng)格,而視圖控制器在屏幕上。 系統(tǒng)提供了內(nèi)置呈現(xiàn)樣式的演示控制器,您可以為自己的呈現(xiàn)樣式提供自定義呈現(xiàn)控制器。
將過(guò)渡委托分配給視圖控制器的transitioningDelegate屬性會(huì)告訴UIKit你要執(zhí)行自定義過(guò)渡或呈現(xiàn)。 您的委托可以選擇性地提供哪些對(duì)象。 如果不提供animator對(duì)象,UIKit在視圖控制器的modalTransitionStyle屬性中使用標(biāo)準(zhǔn)的過(guò)渡動(dòng)畫(huà)。
圖10-1顯示了過(guò)渡委托和動(dòng)畫(huà)對(duì)象與提供的視圖控制器的關(guān)系。 表示控制器僅在視圖控制器的modalPresentationStyle屬性設(shè)置為UIModalPresentationCustom時(shí)使用
圖10-1自定義呈現(xiàn)和動(dòng)畫(huà)對(duì)象
自定義動(dòng)畫(huà)
當(dāng)呈現(xiàn)的視圖控制器的transitioningDelegate屬性包含有效對(duì)象時(shí),UIKit使用您提供的自定義動(dòng)畫(huà)對(duì)象來(lái)呈現(xiàn)視圖控制器。 當(dāng)它準(zhǔn)備一個(gè)呈現(xiàn)視圖時(shí),UIKit調(diào)用animationControllerForPresentedController:presentingController:sourceController:方法轉(zhuǎn)換委托來(lái)檢索自定義animator對(duì)象。 如果對(duì)象可用,UIKit將執(zhí)行以下步驟:
- UIKit調(diào)用轉(zhuǎn)換委托的interactionControllerForPresentation:方法來(lái)查看交互式animator對(duì)象是否可用。 如果該方法返回nil,UIKit執(zhí)行動(dòng)畫(huà)而無(wú)需用戶交互。
- UIKit調(diào)用animator對(duì)象的transitionDuration:方法來(lái)獲取動(dòng)畫(huà)持續(xù)時(shí)間。
- UIKit調(diào)用適當(dāng)?shù)姆椒▉?lái)啟動(dòng)動(dòng)畫(huà):
- 對(duì)于非交互式動(dòng)畫(huà),UIKit會(huì)調(diào)用animator對(duì)象的animateTransition:方法。
- 對(duì)于交互式動(dòng)畫(huà),UIKit調(diào)用交互式動(dòng)畫(huà)對(duì)象的startInteractiveTransition:方法。
- UIKit等待animator對(duì)象調(diào)用上下文轉(zhuǎn)換對(duì)象的completeTransition:方法。
你的自定義動(dòng)畫(huà)在動(dòng)畫(huà)完成之后調(diào)用此方法,通常在動(dòng)畫(huà)的完成塊中。 調(diào)用這個(gè)方法結(jié)束過(guò)渡,讓UIKit知道它可以調(diào)用presentViewController的完成處理程序:animated:completion:方法,并調(diào)用animator對(duì)象自己的animationEnded:方法。
當(dāng)關(guān)閉視圖控制器時(shí),UIKit調(diào)用您的轉(zhuǎn)換委托的animationControllerForDismissedController:方法,并執(zhí)行以下步驟:
- UIKit調(diào)用轉(zhuǎn)換委托的interactionControllerForDismissal:方法來(lái)查看交互式animator對(duì)象是否可用。 如果該方法返回nil,UIKit執(zhí)行動(dòng)畫(huà)而無(wú)需用戶交互。
- UIKit調(diào)用animator對(duì)象的transitionDuration:方法來(lái)獲取動(dòng)畫(huà)持續(xù)時(shí)間。
- UIKit調(diào)用適當(dāng)?shù)姆椒▉?lái)啟動(dòng)動(dòng)畫(huà):
- 對(duì)于非交互式動(dòng)畫(huà),UIKit會(huì)調(diào)用animator對(duì)象的animateTransition:方法。
- 對(duì)于交互式動(dòng)畫(huà),UIKit調(diào)用交互式動(dòng)畫(huà)對(duì)象的startInteractiveTransition:方法。
- UIKit等待animator對(duì)象調(diào)用上下文轉(zhuǎn)換對(duì)象的completeTransition:方法。
- 你的自定義動(dòng)畫(huà)在動(dòng)畫(huà)完成后調(diào)用此方法,通常在動(dòng)畫(huà)的完成塊中。 調(diào)用這個(gè)方法結(jié)束轉(zhuǎn)換,讓UIKit知道它可以調(diào)用presentViewController的完成處理程序:animated:completion:方法,并調(diào)用animator對(duì)象自己的animationEnded:方法。
重要
在動(dòng)畫(huà)結(jié)束時(shí)調(diào)用completeTransition:方法是必需的。 UIKit不會(huì)結(jié)束轉(zhuǎn)換過(guò)
程,從而將控制權(quán)返回到您的應(yīng)用程序,直到您調(diào)用該方法。
過(guò)渡上下文對(duì)象
在過(guò)渡動(dòng)畫(huà)開(kāi)始之前,UIKit創(chuàng)建一個(gè)過(guò)渡上下文對(duì)象,并填充有關(guān)如何執(zhí)行動(dòng)畫(huà)的信息。 過(guò)渡上下文對(duì)象是代碼的重要部分。 它實(shí)現(xiàn)UIViewControllerContextTransitioning協(xié)議,并存儲(chǔ)對(duì)過(guò)渡中涉及的視圖控制器和視圖的引用。 它還存儲(chǔ)有關(guān)如何執(zhí)行過(guò)渡的信息,包括動(dòng)畫(huà)是否是交互式的。 您的animator對(duì)象需要所有這些信息來(lái)設(shè)置和執(zhí)行實(shí)際的動(dòng)畫(huà)。
重要
設(shè)置自定義動(dòng)畫(huà)時(shí),請(qǐng)始終使用轉(zhuǎn)換上下文對(duì)象中的對(duì)象和數(shù)據(jù),而不是您自己管理的
任何緩存信息。 轉(zhuǎn)換可能發(fā)生在各種條件下,其中一些可能會(huì)更改動(dòng)畫(huà)參數(shù)。 轉(zhuǎn)換
上下文對(duì)象保證具有執(zhí)行動(dòng)畫(huà)所需的正確信息,而在調(diào)用動(dòng)畫(huà)師方法時(shí),緩存的信息可
能會(huì)失效。
圖10-2顯示了過(guò)渡上下文對(duì)象如何與其他對(duì)象交互。 你的animator對(duì)象在其animateTransition:方法中接收對(duì)象。 你創(chuàng)建的動(dòng)畫(huà)應(yīng)該在提供的容器視圖中進(jìn)行。 例如,在呈現(xiàn)視圖控制器時(shí),將其視圖添加為容器視圖的子視圖。 容器視圖可能是窗口或常規(guī)視圖,但它始終配置為運(yùn)行動(dòng)畫(huà)。
圖10-2過(guò)渡上下文對(duì)象
過(guò)渡協(xié)調(diào)
對(duì)于內(nèi)置轉(zhuǎn)換和您的自定義轉(zhuǎn)換,UIKit創(chuàng)建一個(gè)轉(zhuǎn)換協(xié)調(diào)器對(duì)象,以便于您可能需要執(zhí)行的任何額外的動(dòng)畫(huà)。 除了視圖控制器的呈現(xiàn)和消除之外,當(dāng)發(fā)生界面旋轉(zhuǎn)時(shí)或當(dāng)視圖控制器的框架改變時(shí),可以發(fā)生過(guò)渡。 所有這些過(guò)渡關(guān)注對(duì)視圖層次結(jié)構(gòu)的改變。 過(guò)渡協(xié)調(diào)器是一種跟蹤這些更改并同時(shí)動(dòng)畫(huà)制作您自己的內(nèi)容的方法。 要訪問(wèn)過(guò)渡協(xié)調(diào)器,請(qǐng)獲取受影響的視圖控制器的transitionCoordinator屬性中的對(duì)象。 過(guò)渡0協(xié)調(diào)器僅存在于過(guò)渡的持續(xù)時(shí)間。
圖10-3顯示了過(guò)渡協(xié)調(diào)器與呈現(xiàn)中涉及的視圖控制器的關(guān)系。 使用過(guò)渡協(xié)調(diào)器獲取有關(guān)過(guò)渡的信息,并注冊(cè)要與過(guò)渡動(dòng)畫(huà)同時(shí)執(zhí)行的動(dòng)畫(huà)塊。 過(guò)渡協(xié)調(diào)器對(duì)象符合UIViewControllerTransitionCoordinatorContext協(xié)議,該協(xié)議提供定時(shí)信息,關(guān)于動(dòng)畫(huà)的當(dāng)前狀態(tài)的信息,以及過(guò)渡中涉及的視圖和視圖控制器。 當(dāng)您的動(dòng)畫(huà)塊被執(zhí)行時(shí),它們類(lèi)似地接收具有相同信息的上下文對(duì)象。
圖10-3過(guò)渡協(xié)調(diào)器對(duì)象
使用自定義動(dòng)畫(huà)呈現(xiàn)視圖控制器
要使用自定義動(dòng)畫(huà)呈現(xiàn)視圖控制器,請(qǐng)?jiān)诂F(xiàn)有視圖控制器的操作方法中執(zhí)行以下操作:
- 創(chuàng)建要顯示的視圖控制器。
- 創(chuàng)建您的自定義過(guò)渡委托對(duì)象并將其分配給視圖控制器的transitioningDelegate屬性。 過(guò)渡委托的方法應(yīng)該在提出時(shí)創(chuàng)建并返回你的自定義動(dòng)畫(huà)對(duì)象。
- 調(diào)用presentViewController:animated:completion:方法來(lái)呈現(xiàn)視圖控制器。
當(dāng)調(diào)用presentViewController:animated:completion:方法時(shí),UIKit啟動(dòng)呈現(xiàn)過(guò)程。 呈現(xiàn)在下一個(gè)運(yùn)行循環(huán)迭代期間開(kāi)始,并繼續(xù),直到您的自定義動(dòng)畫(huà)師調(diào)用completeTransition:方法。 交互式過(guò)渡允許你在過(guò)渡正在進(jìn)行時(shí)處理觸摸事件,但非交互式過(guò)渡在由animator對(duì)象指定的持續(xù)時(shí)間內(nèi)運(yùn)行。
實(shí)現(xiàn)Transitioning Delegate
過(guò)渡委托的目的是創(chuàng)建和返回自定義對(duì)象。 代碼清單10-1顯示了過(guò)渡方法的實(shí)現(xiàn)可以如何簡(jiǎn)單。 此示例創(chuàng)建并返回自定義動(dòng)畫(huà)對(duì)象。 大多數(shù)實(shí)際工作都是由animator對(duì)象本身處理的。
代碼清單10-1創(chuàng)建animator對(duì)象
- (id<UIViewControllerAnimatedTransitioning>)
animationControllerForPresentedController:(UIViewController *)presented
presentingController:(UIViewController *)presenting
sourceController:(UIViewController *)source {
MyAnimator* animator = [[MyAnimator alloc] init];
return animator;
}
過(guò)渡委托的其他方法可以像前面列表中的一樣簡(jiǎn)單。 您還可以結(jié)合自定義邏輯,根據(jù)應(yīng)用程序的當(dāng)前狀態(tài)返回不同的動(dòng)畫(huà)對(duì)象。
實(shí)現(xiàn)您的動(dòng)畫(huà)對(duì)象
animator對(duì)象是采用UIViewControllerAnimatedTransitioning協(xié)議的任何對(duì)象。 animator對(duì)象創(chuàng)建在固定時(shí)間段內(nèi)執(zhí)行的動(dòng)畫(huà)。 animator對(duì)象的關(guān)鍵是它的animateTransition:方法,您可以使用它來(lái)創(chuàng)建實(shí)際的動(dòng)畫(huà)。 動(dòng)畫(huà)過(guò)程大致分為以下幾個(gè)部分:
- 獲取動(dòng)畫(huà)參數(shù)。
- 使用Core Animation或UIView動(dòng)畫(huà)方法創(chuàng)建動(dòng)畫(huà)。
- 清理并完成過(guò)渡。
獲取動(dòng)畫(huà)參數(shù)
傳遞給animateTransition:方法的上下文轉(zhuǎn)換對(duì)象包含執(zhí)行動(dòng)畫(huà)時(shí)要使用的數(shù)據(jù)。 從上下文轉(zhuǎn)換對(duì)象中獲取更多最新信息時(shí),不要使用自己的緩存信息或從視圖控制器獲取信息。 提交和關(guān)閉視圖控制器有時(shí)涉及視圖控制器之外的對(duì)象。 例如,自定義呈現(xiàn)控制器可以添加背景視圖作為呈現(xiàn)視圖的一部分。 上下文過(guò)渡對(duì)象考慮了額外的視圖和對(duì)象,并為您提供了動(dòng)畫(huà)的正確視圖。
- 調(diào)用viewControllerForKey:方法兩次以獲得從過(guò)渡中涉及的“從”和“到”視圖控制器從不假設(shè)你知道哪些視圖控制器參與轉(zhuǎn)換UIKit可能改變視圖控制器,同時(shí)適應(yīng)新的特性 環(huán)境或響應(yīng)你的應(yīng)用程序的請(qǐng)求。
- 調(diào)用containerView方法獲取動(dòng)畫(huà)的superview。 將所有關(guān)鍵子視圖添加到此視圖。 例如,在呈現(xiàn)期間,將呈現(xiàn)的視圖控制器的視圖添加到此視圖。
- 調(diào)用viewForKey:方法來(lái)獲取要添加或刪除的視圖。 視圖控制器的視圖可能不是在過(guò)渡期間添加或刪除的唯一視圖。 表示控制器可能將視圖插入到層次結(jié)構(gòu)中,也必須添加或刪除。 viewForKey:方法返回包含您需要添加或刪除的所有內(nèi)容的根視圖。
- 調(diào)用finalFrameForViewController:方法來(lái)獲取要添加或刪除的視圖的最終框架矩形。
上下文轉(zhuǎn)換對(duì)象使用“from”和“to”命名法來(lái)標(biāo)識(shí)轉(zhuǎn)換中涉及的視圖控制器,視圖和框架矩形。 “從”視圖控制器總是在轉(zhuǎn)換開(kāi)始時(shí)在屏幕上的視圖,并且“到”視圖控制器是其視圖在轉(zhuǎn)換結(jié)束時(shí)可見(jiàn)的那個(gè)。 如圖10-4所示,“from”和“to”視圖控制器在顯示和關(guān)閉之間切換位置。
圖10-4 from和to對(duì)象
交換值使得更容易編寫(xiě)處理呈現(xiàn)和解除的單個(gè)動(dòng)畫(huà)。 當(dāng)你設(shè)計(jì)你的動(dòng)畫(huà)師時(shí),你需要做的是包含一個(gè)屬性來(lái)知道它是動(dòng)畫(huà)的演示還是解雇。 兩者之間唯一需要的區(qū)別如下:
- 對(duì)于呈現(xiàn),將“到”視圖添加到容器視圖層次結(jié)構(gòu)中。
- 對(duì)于關(guān)閉,從容器視圖層次結(jié)構(gòu)中刪除“from”視圖。
創(chuàng)建過(guò)渡動(dòng)畫(huà)
在典型的呈現(xiàn)期間,將屬于呈現(xiàn)的視圖控制器的視圖動(dòng)畫(huà)化到位。 其他視圖可能會(huì)作為呈現(xiàn)視圖的一部分進(jìn)行動(dòng)畫(huà)處理,但是動(dòng)畫(huà)的主要目標(biāo)是將視圖添加到視圖層次結(jié)構(gòu)中。
當(dāng)對(duì)主視圖進(jìn)行動(dòng)畫(huà)處理時(shí),您配置動(dòng)畫(huà)所需的基本操作是相同的。 從過(guò)渡的上下文對(duì)象中獲取所需的對(duì)象和數(shù)據(jù),并使用該信息創(chuàng)建實(shí)際的動(dòng)畫(huà)。
-
呈現(xiàn)動(dòng)畫(huà)
- 使用viewControllerForKey:和viewForKey:方法來(lái)檢索過(guò)渡中涉及的視圖控制器和視圖。
- 設(shè)置“from”視圖的起始位置。 將任何其他屬性設(shè)置為其起始值。
- 從上下文過(guò)渡上下文的finalFrameForViewController:方法中獲取“to”視圖的結(jié)束位置。
- 將“to”視圖添加為容器視圖的子視圖。
- 創(chuàng)建動(dòng)畫(huà)。
- 在動(dòng)畫(huà)塊中,將“to”視圖動(dòng)畫(huà)化到容器視圖中的最終位置。 將任何其他屬性設(shè)置為其最終值。
- 在完成塊中,調(diào)用completeTransition:方法,并執(zhí)行任何其他清理。
-
關(guān)閉動(dòng)畫(huà):
- 使用viewControllerForKey:和viewForKey:方法來(lái)檢索過(guò)渡中涉及的視圖控制器和視圖。
- 計(jì)算“from”視圖的結(jié)束位置。 此視圖屬于提交的視圖控制器,現(xiàn)在正在被關(guān)閉。
- 將“to”視圖添加為容器視圖的子視圖。在呈現(xiàn)期間,當(dāng)過(guò)渡完成時(shí),移除屬于呈現(xiàn)視圖控制器的視圖。 因此,您必須在關(guān)閉操作期間將該視圖添加回容器。
- 創(chuàng)建動(dòng)畫(huà)。
- 在動(dòng)畫(huà)塊中,將“from”視圖動(dòng)畫(huà)化到容器視圖中的最終位置。 將任何其他屬性設(shè)置為其最終值。
- 在完成塊中,從視圖層次結(jié)構(gòu)中刪除“from”視圖,并調(diào)用completeTransition:方法。 根據(jù)需要執(zhí)行任何其他清理。
圖10-5顯示了一個(gè)自定義的顯示和關(guān)閉過(guò)渡,動(dòng)畫(huà)是它的對(duì)角視圖。 在呈現(xiàn)期間,呈現(xiàn)的視圖從屏幕開(kāi)始,向上和向左對(duì)角線動(dòng)畫(huà),直到它可見(jiàn)。 在關(guān)閉期間,視圖反轉(zhuǎn)其方向,向下和向右動(dòng)畫(huà),直到它再次離屏。
圖10-5自定義呈現(xiàn)和關(guān)閉
代碼清單10-2顯示了如何實(shí)現(xiàn)圖10-5中所示的過(guò)渡。 檢索動(dòng)畫(huà)所需的對(duì)象后,animateTransition:方法計(jì)算受影響視圖的框架矩形。 在呈現(xiàn)期間,所呈現(xiàn)的視圖由toView變量表示。 在關(guān)閉中,已解除的視圖由fromView變量表示。 presentation屬性是animator對(duì)象本身的一個(gè)自定義屬性,過(guò)渡委托在創(chuàng)建animator時(shí)設(shè)置為一個(gè)合適的值。
代碼清單10-2用于實(shí)現(xiàn)對(duì)角呈現(xiàn)和關(guān)閉的動(dòng)畫(huà)
- (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];
}];
}
清理后的動(dòng)畫(huà)
在過(guò)渡動(dòng)畫(huà)結(jié)束時(shí),至關(guān)重要的是調(diào)用completeTransition:方法。 調(diào)用該方法告訴UIKit轉(zhuǎn)換完成,并且用戶可以開(kāi)始使用呈現(xiàn)的視圖控制器。 調(diào)用該方法也會(huì)觸發(fā)其他完成處理程序的級(jí)聯(lián),包括來(lái)自presentViewController:animated:completion:方法和animator對(duì)象自己的animationEnded:方法。 調(diào)用completeTransition:方法的最好的地方是在你的動(dòng)畫(huà)塊的完成處理程序中。
因?yàn)檫^(guò)渡可以取消,所以您應(yīng)該使用上下文對(duì)象的transitionWasCancelled方法的返回值來(lái)確定需要清除。 取消呈現(xiàn)視圖時(shí),您的動(dòng)畫(huà)必須撤消對(duì)視圖層次結(jié)構(gòu)所做的任何修改。 關(guān)閉的成功需要類(lèi)似的操作。
向你的過(guò)渡添加交互性
使動(dòng)畫(huà)交互的最簡(jiǎn)單的方法是使用UIPercentDrivenInteractiveTransition對(duì)象。 UIPercentDrivenInteractiveTransition對(duì)象與你現(xiàn)有的動(dòng)畫(huà)對(duì)象配合使用,以控制其動(dòng)畫(huà)的時(shí)間。 它使用你提供的完成百分比值。 所有你需要做的是設(shè)置計(jì)算完成百分比值所需的事件處理代碼,并在每個(gè)新事件到達(dá)時(shí)更新它。
您可以使用帶有或不帶有子類(lèi)的UIPercentDrivenInteractiveTransition類(lèi)。 如果是子類(lèi),使用子類(lèi)的init方法(或startInteractiveTransition:方法)來(lái)執(zhí)行事件處理代碼的一次性設(shè)置。 之后,使用自定義事件處理代碼計(jì)算新的完成百分比值,并調(diào)用updateInteractiveTransition:方法。 當(dāng)代碼確定過(guò)渡完成時(shí),調(diào)用finishInteractiveTransition方法。
代碼清單10-3顯示了UIPercentDrivenInteractiveTransition子類(lèi)的startInteractiveTransition:方法的自定義實(shí)現(xiàn)。 此方法設(shè)置一個(gè)搖動(dòng)手勢(shì)識(shí)別器來(lái)跟蹤觸摸事件,并將該手勢(shì)識(shí)別器安裝在動(dòng)畫(huà)的容器視圖上。 它還保存對(duì)過(guò)渡上下文的引用以供以后使用。
代碼清單10-3配置百分比驅(qū)動(dòng)交互式動(dòng)畫(huà)設(shè)計(jì)器
- (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];
}
手勢(shì)識(shí)別器為到達(dá)的每個(gè)新事件調(diào)用其動(dòng)作方法。 您的Action方法的實(shí)現(xiàn)可以使用手勢(shì)識(shí)別器的狀態(tài)信息來(lái)確定手勢(shì)是成功,失敗還是仍在進(jìn)行中。 同時(shí),您可以使用最新的觸摸事件信息來(lái)計(jì)算手勢(shì)的新百分比值。
代碼清單10-4顯示了由代碼清單10-3中配置的平移手勢(shì)識(shí)別器調(diào)用的方法。 當(dāng)新事件到達(dá)時(shí),該方法使用垂直行程距離來(lái)計(jì)算動(dòng)畫(huà)的完成百分比。 當(dāng)手勢(shì)結(jié)束時(shí),該方法結(jié)束過(guò)渡。
代碼清單10-4使用事件更新動(dòng)畫(huà)進(jìn)度
-(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];
}
}
注意
您計(jì)算的值表示動(dòng)畫(huà)的整個(gè)長(zhǎng)度的完成百分比。 對(duì)于交互式動(dòng)畫(huà),您可能需要避免非
線性效應(yīng),例如動(dòng)畫(huà)本身中的初始速度,阻尼值和非線性完成曲線。 這種效應(yīng)傾向于
將事件的觸摸位置與任何下層視圖的移動(dòng)分離。
創(chuàng)建與過(guò)渡一起運(yùn)行的動(dòng)畫(huà)
過(guò)渡中涉及的視圖控制器可以在任何呈現(xiàn)或過(guò)渡動(dòng)畫(huà)之上執(zhí)行其他動(dòng)畫(huà)。 例如,呈現(xiàn)的視圖控制器可以在過(guò)渡期間對(duì)其自己的視圖層級(jí)進(jìn)行動(dòng)畫(huà)化,并且在發(fā)生過(guò)渡時(shí)添加運(yùn)動(dòng)效果或其他視覺(jué)反饋。 任何對(duì)象都可以創(chuàng)建動(dòng)畫(huà),只要它能夠訪問(wèn)呈現(xiàn)或呈現(xiàn)視圖控制器的transitionCoordinator屬性即可。 過(guò)渡協(xié)調(diào)器僅在轉(zhuǎn)換正在進(jìn)行時(shí)存在。
要?jiǎng)?chuàng)建動(dòng)畫(huà),請(qǐng)調(diào)用過(guò)渡協(xié)調(diào)器的animateAlongsideTransition:completion:或animateAlongsideTransitionInView:animation:completion:方法。 您提供的塊將被存儲(chǔ),直到過(guò)渡動(dòng)畫(huà)開(kāi)始,此時(shí)它們與其余的過(guò)渡動(dòng)畫(huà)一起執(zhí)行。
呈現(xiàn)控制器與你的動(dòng)畫(huà)
對(duì)于自定義呈現(xiàn),您可以提供自己的呈現(xiàn)控制器,為所呈現(xiàn)的視圖控制器提供自定義外觀。 呈現(xiàn)控制器管理與視圖控制器及其內(nèi)容分離的任何自定義。 例如,放置在視圖控制器視圖后面的調(diào)光視圖將由呈現(xiàn)控制器管理。 事實(shí)上,它不管理特定的視圖控制器的視圖意味著您可以使用相同的呈現(xiàn)控制器與您的應(yīng)用程序中的任何視圖控制器。
你從呈現(xiàn)的視圖控制器的過(guò)渡委托提供自定義的顯示控制器。 (視圖控制器的modalTransitionStyle屬性必須是UIModalPresentationCustom。)呈現(xiàn)控制器與任何動(dòng)畫(huà)對(duì)象并行操作。 由于動(dòng)畫(huà)師對(duì)象將視圖控制器的視圖動(dòng)畫(huà)化到位,呈現(xiàn)控制器將任何附加視圖動(dòng)畫(huà)化到位。 在過(guò)渡結(jié)束時(shí),呈現(xiàn)控制器有機(jī)會(huì)對(duì)視圖層次執(zhí)行任何最終調(diào)整。