objc系列譯文(5.3):檢視控制器轉場

studentdeng發表於2013-11-20

自定義動畫

iOS 7 對我來說最激動人心的特性就是新的檢視控制器切換API(View Controller Transitioning API)。 iOS 7 之前,View Controller之間切換,我需要建立自定義的transitions。 而且這些方法都支援不完整,讓人頭疼。在transitions中增加互動功能就更難了。

在開始這篇文章之前,我要提醒一下:這是一個新的API,我們盡最大努力讓它可以實用,但是並不能保證是最佳。可能需要至少一個月後才能確定,這篇文章不是最佳的實用方案,這裡只是一個對新功能的探索。如果有更好的使用這個API的方法,請聯絡我們,這樣就可以修正這篇文章。

在開始介紹這個API之前,我們需要知道導航控制器的預設行為在iOS7下已經改變了:導航控制器下,切換2個view controller的動畫有一點細微的改變,變得更有互動性。例如,當你希望彈出一個view controller時,可以從螢幕左邊開始拖動,把整個內容拖動到螢幕右邊。

讓我們仔細看一下這個API,我發現這個被重度使用的介面是協議並不是一個實體。雖然一上來看上去有一點怪,但是我喜歡這個API,它給了我們更多的靈活性。我們從簡單開始:用自定義動畫代替原有的view controller的push動畫(這裡是sample project 在github)。我們首先需要實現這個新的 UINavigationControllerDelegate 方法:

我們可以觀察一下這種型別的操作(push 和 pop)返回一個不同的 animator。如果我們分享程式碼的話,這個可能是一個物件。我們可能需要把這個變數通過property儲存下來。我們也可以為不同的操作建立不同的物件,這裡有很高的靈活性。

讓這個動畫執行起來,我們建立一個自定義物件實現 UIViewControllerContextTransitioning 協議。

這個協議要求我們實現2個方法,其中一個是描述動畫的執行時間

另一個是描述動畫的執行。

這裡你可以看到這個協議是怎麼用的:沒有提供實體的物件引數,而是通過這個型別 id 得到transitionContext 唯一的最重要的東西就是在完成動畫之後要呼叫 completeTransition 這個告訴 transitionContext 我們已經完成動畫並且相應的更新了 view controller的狀態。其他程式碼是標準的,我們通過transitionContext得到2個UIViewController,然後使用簡單的 UIView 動畫,這裡我們很簡單的做了一個zooming的動畫

注意,我們只是寫了push的自定義動畫,當view controller pop時,iOS系統還是會使用預設的滑動動畫。而且,實現這個方法後。導航欄也不能互動了(就是從左到右拖動實現pop view controller)。下面完善它

互動動畫

讓之前的動畫變得能夠互動起來非常簡單。我們需要實現另一個UINavigationControllerDelegate

注意,如果在一個不能互動的動畫中,這裡會返回nil。(譯註:當不能互動時 self.interactionController 為 nil)

interactionController是UIPercentDrivenInteractionTransition的例項,沒有必要更多的設定。我們通過建立拖動手勢(UIPanGestureRecognizer)來實現:

只有當使用者在螢幕右邊操作時,我們才設定動畫是可以互動的(通過設定interactionController 屬性)。然後我們呼叫performSegueWithIdentifier(或是不用storyboards,直接push view controller) 在這個手勢變化中,我們呼叫interactionController 的一個方法 updateInteractiveTransition:

這裡根據拖動的距離設定百分比,非常cool的事情是互動控制器(interactionController)和 動畫控制器(animation controller)相互協作。而且因為是普通的 UIView 動畫,它控制著動畫的程式。我們不需要處理他們之前的事情, 所有的事情都在背後默默的自動搞定了。

最後,當手勢停止或是取消掉,我們需要呼叫interaction controller相應的方法

當切換動畫完畢時,設定interactionController為nil非常重要。如果下一個動畫是非互動的,我們不希望得到一個奇怪的 interactionController

現在我們已經有一個完整的自定義的可互動的過度變換(transition)了。通過普通的拖動手勢和一個UIKit提供的實體物件,幾行程式碼就搞定了。對於大多數的自定義互動過度變換,你可以在這裡停下來,用上面提到的方法做任何你想做得動畫 或是互動。

GPUImage自定義動畫

我們現在已經能夠實現一個完整的自定義動畫了,可以不用UIView 甚至Core Animation,做自己喜歡的動畫。一開始,我用Core Image實現了一個專案Letterpress-style。但是在我的舊iPhone4上面只能跑到大約9FPS,這個和我所期望的60FPS差距太大了。

但是當我使用GPUImage後,實現一個非常漂亮的自定義動畫效果變得非常簡單。我們希望這個動畫能夠做到畫素級的消融在2個view controller切換的時候。這個是通過分別對2個view controller 截圖,然後應用GPUImage的圖片濾鏡實現的。

首先,我們建立一個自定義類,實現animation 和 interactive transition 協議。

為了讓這個動畫跑的飛快,我們只把圖片傳給GPU一次,然後把所有的影像處理繪製交給GPU,而不是傳給CPU(GPU和CPU之間的資料傳輸非常慢)。通過GPUImageView,我們可以用OpenGL繪製動畫效果(不需要手動編寫底層的OpenGL程式碼,我們可以繼續編寫上層程式碼)

建立這樣的濾鏡鏈非常方便。這裡可以看一下下面的例子。有一點挑戰的是實現動態的濾鏡。GPUImage不能給我們直接提供動畫效果。這裡我們通過在每一幀的時候更新濾鏡來實現動畫的繪製。我們使用CADisplayLink類來做這個。

在frame:方法中,我們根據時間更新動畫進度,然後更新濾鏡

以上就是我們所有要講得了。在互動變換中,我們需要確保我們的進度是根據手勢識別設定的,而不是根據時間。但是剩下的程式碼幾乎都一樣了。

這個真的太強大了,你可以使用GPUImage提供的任何濾鏡或是自己寫的OpenGL程式碼來實現上面的效果。

 

小結

我們這裡僅僅提到了導航控制器下面的2個 view controller 之間的動畫,事實上你可以做相同的事情在tabbar controller 或是自定義的container view controller。而且 UICollectionViewController 現在已經可以在layout上面自動實現互動動畫了。他們都是使用相同的機制。這個真的太強大了。

當我和Orta提到這個API時,他指出他已經使用這個功能建立了一些輕量級的view controller。不要在每一個view controller 儲存管理動畫的程式碼,而是建立一個新的view controller,然後實現2個view controlller檢視切換時的自定義的動畫效果。

 

更多

相關文章