iOS 按鈕動畫

林欣達發表於2016-09-12

前言

在上一篇《認識CoreAnimation》中筆者介紹了系統的動畫庫CoreAnimation,使用動畫庫有很多好處,這裡就不再進行重複敘述。那麼本篇將承接上一篇的內容,使用提到的基礎的動畫相關類來實現動畫效果,效果圖放上:

iOS 按鈕動畫

大體上可以看到demo主要是漸變以及形變兩種動畫,在更早之前的文章,我們就使用UIView的動畫介面完成過相同的動畫,而這次將換成CoreAnimation來完成這些工作

關於圖層

在iOS中,每一個UIView都擁有一個與之繫結的CALayer圖層物件,其負責檢視內容的繪製與顯示。跟前者一樣,CALayer也擁有樹狀的子圖層結構,以及相似的介面方法。CALayer是圖層的基類,主要提供了檢視顯示範圍、圖層結構介面等屬性,我們通過使用它的子類。下面是一段在控制器的介面中心新增一個圓形的紫色圖層:

同樣的,每一個CALayer存在一個sublayers的陣列屬性,我們也可以遍歷這個陣列來完成移除子檢視之類的操作:

由於核心動畫框架的動畫都是基於CALayer的圖層進行新增實現的,所以圖層的新增移除方法是最常用的方法。當然,還有一個addAnimation(anim:forKey:)介面用來給圖層新增動畫

基礎動畫

基礎動畫CABasicAnimation是最常用來實現動畫效果的動畫類,其繼承自CAAnimation動畫基類,為圖層動畫效果實現了一個keyPath屬性,我們通過設定這個屬性來為對應的keyPath屬性值執行動畫效果。動畫類提供了fromValuetoValue兩個屬性用來設定動畫的起始和結束的值,比如下面一段程式碼讓新增到檢視上的紫色圖層變得透明:

iOS 按鈕動畫

上面的程式碼用動畫表現了在1秒內讓圖層的opacity屬性從10的過程。但上面不難看出在動畫結束之後,紫色的圖層沒有保持opacity等於0的狀態,而是回到了動畫最開始的狀態。這是為什麼呢?

在上一篇中筆者提到過在每一個CALayer中存在著模型呈現渲染三種圖層樹,正是這些圖層樹共同作用來完成隱式動畫。那麼使用核心動畫的時候,實際上CABasicAnimation會根據動畫時長計算出每一幀的動畫屬性的值,然後實時提交給呈現樹來展示對應時間點的檢視效果,在動畫結束時CAAnimation物件會自動從圖層上移除。而由於在整個動畫過程模型樹的值沒有改變,所以在動畫結束的時候呈現樹會再次從模型樹獲取圖層的屬性重新繪製。對此,存在這幾種解決方案:

  • 在實現動畫的時候同時修改opacity,保證模型樹的資料同步
  • 取消CAAnimation的自動移除,並且設定在動畫結束後保持動畫的結束狀態
  • 實現動畫代理方法。綜合上面兩種方法的操作

相比較前兩種方法,實現代理然後設定屬性的做法有些繁雜且無用的感覺。但在某些應用場景下,我們需要在動畫結束時移除圖層或其他操作,通過實現代理是最好的做法。其他常用的keyPath動畫值可以在這裡檢視

動畫組

接著上面的動畫效果,我想要在漸變的基礎上增加一個形變動畫,那麼我需要建立兩個CABasicAnimation物件來完成這一工作:

除了上面這段程式碼之外,在CoreAnimation框架中提供了一個CAAnimationGroup類來將多個動畫物件整合成一個物件新增到圖層上。從使用實現的角度而言,並不會跟上面的程式碼有任何出入,卻可以讓程式碼的邏輯更加清晰:

按鈕動畫

首先是動畫中的形變和透明漸變分別對應transform以及opacity兩個keyPath,其次,動畫圖層不是按鈕本身的圖層,因此還需要新增額外的一個圖層。另外,動畫存在外擴和內擴的動畫效果,因此我們還需要定義一個列舉來區分:

在swift的extension中不支援新增儲值屬性,因此我們需要使用到runtime的動態繫結來完成對按鈕包括動畫型別、動畫顏色兩個屬性的擴充:

接下來是如何保證我們在點選按鈕的時候可以執行我們的動畫。這裡我們通過重寫按鈕的sendAction(action:to:forEvent:)方法來執行動畫,這個方法在每次按鈕傳送一個事件時會被呼叫。同理,當使用者點選按鈕時也會呼叫這個方法:

擴充套件之後的按鈕只要設定animationType這個屬性之後就會實現在點選時的動畫效果

尾話

相比起國外的應用,國內的動畫效果要顯得內斂得多,甚至很多的app是沒考慮過動畫製作的。但是在移動端開發已然是一片血海的今天,漂亮的動畫效果仍然會為你的應用帶來留存,前提是你的應用要靠譜——單純的動效留不住人。因此,掌握動畫是至關重要的一項基本技能。本文demo

上一篇:《iOS動畫-認識CoreAnimation》

相關文章