ViewController編程指南展示和過(guò)渡-自定義過(guò)渡動(dòng)畫(huà)

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

VCPG_custom-presentation-and-animator-objects_10-1_2x.png

自定義動(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í)行以下步驟:

  1. UIKit調(diào)用轉(zhuǎn)換委托的interactionControllerForPresentation:方法來(lái)查看交互式animator對(duì)象是否可用。 如果該方法返回nil,UIKit執(zhí)行動(dòng)畫(huà)而無(wú)需用戶交互。
  2. UIKit調(diào)用animator對(duì)象的transitionDuration:方法來(lái)獲取動(dòng)畫(huà)持續(xù)時(shí)間。
  3. 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:方法。
  4. 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í)行以下步驟:

  1. UIKit調(diào)用轉(zhuǎn)換委托的interactionControllerForDismissal:方法來(lái)查看交互式animator對(duì)象是否可用。 如果該方法返回nil,UIKit執(zhí)行動(dòng)畫(huà)而無(wú)需用戶交互。
  2. UIKit調(diào)用animator對(duì)象的transitionDuration:方法來(lái)獲取動(dòng)畫(huà)持續(xù)時(shí)間。
  3. 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:方法。
  4. UIKit等待animator對(duì)象調(diào)用上下文轉(zhuǎn)換對(duì)象的completeTransition:方法。
  5. 你的自定義動(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ì)象

VCPG_transitioning-context-object_10-2_2x.png

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

![VCPG_from-and-to-objects_10-4_2x.png](http://upload-images.jianshu.io/upload_images/133280-17ae5755207363a3.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

使用自定義動(dòng)畫(huà)呈現(xiàn)視圖控制器

要使用自定義動(dòng)畫(huà)呈現(xiàn)視圖控制器,請(qǐng)?jiān)诂F(xiàn)有視圖控制器的操作方法中執(zhí)行以下操作:

  1. 創(chuàng)建要顯示的視圖控制器。
  2. 創(chuàng)建您的自定義過(guò)渡委托對(duì)象并將其分配給視圖控制器的transitioningDelegate屬性。 過(guò)渡委托的方法應(yīng)該在提出時(shí)創(chuàng)建并返回你的自定義動(dòng)畫(huà)對(duì)象。
  3. 調(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è)部分:

  1. 獲取動(dòng)畫(huà)參數(shù)。
  2. 使用Core Animation或UIView動(dòng)畫(huà)方法創(chuàng)建動(dòng)畫(huà)。
  3. 清理并完成過(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)閉

VCPG_custom-presentation-and-dismissal_10-5_2x.png

代碼清單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)整。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容