翻譯:iOS視圖控制器編程指南(十)——自定義過(guò)渡動(dòng)畫(huà)(Customizing the Transition Animations)

過(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控制器。

圖10-1 自定義present和動(dòng)畫(huà)對(duì)象

關(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í)行以下步驟:

  1. UIKit調(diào)用過(guò)渡代理的 interactionControllerForPresentation:方法,查看是否有可用的交互動(dòng)畫(huà)對(duì)象。如果該方法返回nil,UIKit執(zhí)行沒(méi)有交互的動(dòng)畫(huà)。
  2. UIKit調(diào)用動(dòng)畫(huà)對(duì)象的 transitionDuration: 方法來(lái)獲取動(dòng)畫(huà)持續(xù)時(shí)間。
  3. 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:方法。
  4. 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í)行以下步驟:

  1. UIKit調(diào)用過(guò)渡代理的 interactionControllerForDismissal:方法,查看是否有可用的交互式動(dòng)畫(huà)對(duì)象。如果該方法返回nil,UIKit執(zhí)行沒(méi)有交互的動(dòng)畫(huà)。
  2. UIKit調(diào)用動(dòng)畫(huà)對(duì)象的 transitionDuration: 方法來(lái)獲取動(dòng)畫(huà)持續(xù)時(shí)間。
  3. 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:方法。
  4. 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à)。

圖10-2 過(guò)渡環(huán)境對(duì)象

關(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ì)象。

圖10-3 過(guò)渡協(xié)調(diào)器對(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)作方法:

  1. 創(chuàng)建想要present的視圖控制器。
  2. 創(chuàng)建自定義過(guò)渡代理對(duì)象,并將其分配給視圖控制器的 transitioningDelegate屬性。必須創(chuàng)建過(guò)渡代理的方法,在調(diào)用時(shí)返回自定義動(dòng)畫(huà)對(duì)象。
  3. 調(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è)部分:

  1. 獲取動(dòng)畫(huà)參數(shù)。
  2. 使用核心動(dòng)畫(huà)或 UIView 動(dòng)畫(huà)方法創(chuàng)建動(dòng)畫(huà)。
  3. 清理和完成過(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í)交換位置。

圖10-4 “from”和“to”對(duì)象

交換值使編寫(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ò)程中,必須添加該視圖到容器視圖控制器。

  1. 在動(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-5 自定義present和dismiss

列表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)。

官方原文地址:

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容