# 序
好的動畫吸引目光,好的手勢留住使用者。
在手機螢幕越來越大的今天,好的手勢操作可以幫助使用者在單手的情況下依然能完成多數的操作。
最簡單的🌰就是,iOS在過去的習慣是把 close 放在右上角 (back 在左上),在賈伯斯時代的 iPhone4、4s 或者在 5/5s/SE的機種上,都還是可以單手輕鬆的完成關閉的操作。
但是時代在進步,顯然的,人類手掌大小的進化速度已經跟不上手機螢幕大小的進化速度了。
那麼如何讓使用者可以輕鬆的完成 close 這個動作呢?手勢顯然是個不錯的選擇!
對應一般關閉這個動作所產生的動畫,在 iOS 中習慣是:⬇️ 由上往下消失。
那麼在不影響頁面操作的情況下,加上一個由上往下拖移的手勢,來讓畫面 關閉,是可以幫助使用者輕鬆的完成關閉這個動作。
# 熱身
在說明自定義手勢之前,我們先讓原生的 present & dismiss 更加的華麗吧(?
動畫的原理是使用 maskView 畫出一個圓,然後對圓進行放大縮小
這邊使用 maskView 的原因是:在背景比較複雜的情況下(非純色),變化的效果會比較清晰,比較符合我個人的喜好 :)
那要自定義轉場動畫,就要先了解,在 iOS 中是如何實現自訂的轉場動畫
看上圖可以發現,由A到B,似乎不是我們視覺上看到的,移動B畫面。
其實動畫是在中間一個 Container View 中實作動畫
當動畫完成的時候,Containter View 中看到的畫面會跟B畫面完全一致,這樣在把 Container View 移除,就可以神不知鬼不覺的,達到一種,B畫面在移動的錯覺
# Engine start
首先我們先建立一個 class 用來管理我們 present & dismiss 的動畫要透過誰來動。
點UIViewControllerTransitioningDelegate
進去看,可以發現裡面有五個 func 都是 optional
,是這樣的,
如果不實作,那系統將會使用原生的效果來顯示。
我在網路上查的多數範例,是直接在 class CustomTransition
實作這個 protocol 然後 return self
,但為了讓 code 看起來簡單一點,我選擇另外建立兩個 class confirm 這個 protocol
分別是管理 present & dismiss 的兩個 class,會這麼做的原因:方便在不同的動畫間做組合;或者只單獨使用某個客製動畫,另外一個使用原生動畫。
首先把框框架好:
接著我們在 func animateTransition
裡面實作我的們想要的動畫效果
Dismiss 其實差不多,只是把動畫反過來 run
東西都建立好啦,接著就是如何使用了,我們在A畫面建立 CustomTransition,然後將B畫面的 transitioningDelegate 改為 CustomTransition, 並將 modalPresentationStyle 設置為 .custom
# Move your body
原本的手勢是由系統來處理(其實也只有 Navigation 的返回手勢..),所以我們需要通知系統:手勢交由我們建立的 CustomTransition
來處理。
這邊使用 interaction.interacting ? interaction : nil
這樣的寫法,翻成中文大概的意思是:
如果已經在處理互動了,那就不用多嘴跟系統說誰來處理互動。
所以在 UpToDownIneraction 裡面我們宣告一個 interacting 來負責是否在互動中。
注意這兩個 func 在 UIViewControllerTransitioningDelegate 裡面其實是 optional 的,如果不實做的話,系統會使用原生的手勢。ex: navigation 會有右滑返回的手勢。
那這邊只使用 interactionControllerForDismissal ,也就是 dismiss 需要的透過誰來管理。因為在出現的部分如果使用手勢,不熟悉的使用者可能會產生疑惑,除非 UI 上有明顯的提示,例如一個箭頭,加上一個抖動的動畫,不然使用點擊事件來當作出現的觸發條件會是比較好的選擇。
接下來就是實作互動管理了
先把 class 建立起來,並寫好我們需要的 property
可能有人發現我們繼承的 class 是 UIPercentDrivenInteractiveTransition
而不上面 func 需要回傳的 UIViewControllerInteractiveTransitioning
這是蘋果提供的一個類別,他是基於 UIViewControllerInteractiveTransitioning
的封裝,如果動畫中使用的是 UIView.animation
,那我們就只需要告訴系統現在要 run 到第幾 % ,蘋果爸爸會幫我們處理這一趴的動畫應該是長什麼樣子。
- interacting 是管理是否在互動中
- couldComplete 負責的是,如果滑到一半,就放開手指,這時候是要跑完整個動畫,還是恢復動畫前的狀態。
- presentingViewController 是我們需要加上手勢的 ViewController
(除了配合手勢,執行動畫,還需要跟系統說我們要 dismiss 還是 pop,所以需要一個 property 存起來方便之後判斷)
然後實作我們需要的方法
首先加上手勢,並做好手勢需要執行的空方法
接著實作手勢執行的方法, UIPercentDrivenInteractiveTransition
類別提供了我們幾個方法
- update(_ percentComplete: CGFloat)
- cancel()
- finish()
很明顯我們只需要算好 手勢在畫面上移動的比例
,然後使用 update 來更新,並在手指放開的時後跟系統說我們要 取消
或是 完成
動畫,即可。
484很簡單?這邊特別提醒一下,如果動畫不是使用 UIView.animation 來實作,那麽 UIPercentDrivenInteractiveTransition
就不適用了。這個坑害我搞了好久….
接著就是在 present 的地方加上我們寫好的手勢啦
完整的例子 🌰 在 Github 。
也歡迎到我的 個人網站 逛逛。
我是果思設計的 iOS 工程師 ohlulu,我們專注在 App 設計與 App 開發,希望文章對各位有幫助!非常期待在留言區與大家討論、吸收更多觀點:)