Swift — 實作 The UX in Motion Manifesto 的動畫效果 Part.2

Jeremy Xue
11 min readJun 22, 2018

--

前導

我以為我能一天介紹大概接近一半的動畫效果,結果實際只錯了兩個 QQ

動畫完成的過程真的是挺繁複的,還要顧慮到許多因素,例如:發生時間、過程發生什麼、小細節效果之類的。但做足這麼多努力之後,發現動畫效果跑不到一秒就結束了…,但我還是喜歡動畫效果呈現出來時讓人驚訝的樣子。

今天我們也繼續接著介紹下去這 12 實用的動畫效果,如果還沒看過上一篇的話可以先看過一輪:

# Parenting(從屬關係)

與多個物體互動時,創造空間和時間上的階層關係

Parenting 效果讓元件間有互相關聯,是很常用且實用的原理。Parenting 會讓物件之間的關聯,以會增加易用性的方式產生物體關聯和結構。Parenting 也可以讓設計師在大部分情況下更好安排介面事件,與使用者溝通時更自然描述物體的關聯。

這邊我們實作兩個範例,第一個範例我們先實作原文裡面所提到的一個簡單的評分介面的感覺,使用者可以透過 Slider 調整評比:

Parenting 動畫效果展示

開始實作:

#1 Slider 控制品質

開始畫面

我們首先開始實作第一個 Slider 評分功能,這邊我們希望除了 icon 顯示笑臉外,我們還順便加上一個 Label 來表示這個心情,使 Slider 與上面兩個元件有關聯,我們先建立好我們的 Slider 所需的最大最小值以及初始值:

設定 Slider 預設數值

接著我們先設定好當 Slider 的 value 會變更所需的值:

預先設好設定好每個值顯示的資料

我們可以知道我們這個 Slider 調整會進行兩種方法,第一個是「 位移 」,第二個是「 變色 」效果,所以我們接著定義下面兩個方法:

  • 位移動畫:
func moveX(value:Int,object:UILabel) {  // 取 slider 7分之1 的寬度,作為我們位移距離
let xMove = rateSlider.frame.width / 7
UIView.animate(withDuration: 0.2) { object.center.x += CGFloat(value) * xMove }}
  • 變色效果:
func updateUI(value:Int) {  let number = value - 1  UIView.animate(withDuration: 0.2) {    // 進行各個 UI 元件的顏色改變
self.rateSlider.tintColor = self.colorArray[number]
self.rateLabel.layer.borderColor = self.colorArray[number].cgColor
self.rateLabel.textColor = self.colorArray[number]
self.rateLabel.text = self.textArray[number]
self.emojiLabel.text = self.emojiArray[number]
self.emojiLabel.backgroundColor = self.colorArray[number]
}}

這時我們兩個方法都定義完成了,最後我們將我們 slider 與程式碼進行一個 Action 連結,選擇「 Value Change 」,這樣我們最後只要在 Value Change 中將我們 Slider 的 Value 經由計算傳遞到我們剛剛定義的方法中:

@IBAction func valueChange(_ sender: UISlider) {  // 將 slider 的值四捨五入到 value 中。
let value = lrintf(sender.value)
// 判斷要往左或是往右位移
if value > lastValue {
moveX(value: value - lastValue, object: emojiLabel)
moveX(value: value - lastValue, object: rateLabel)
} else { moveX(value: -(lastValue - value), object: emojiLabel)
moveX(value: -(lastValue - value), object: rateLabel)
} // 更新 UI 顏色
updateUI(value: value)
// 更新上一個值
lastValue = value
// 將 Slider 位置移動到我們四捨五入後的值上
rateSlider.value = Float(value)
}
使用 Slider 調整服務品質

#2 點擊按鈕顯示個人頁面

Parenting 動畫效果展示

這邊我們一樣也操作與他相似的效果,我們下面是點選貓咪頭像來變更後面的背景圖片,並且點選的貓咪頭像要放大並且移動到中間:

點選貓咪頭像變更背景

我們一樣定義一個貓咪名稱的 Array,以及點選到的按鈕 tag:

設定好預設字串以及存放按鈕 tag 的變數

所以我們需要定義變更背景圖片以及位移放大按鈕的效果,首先我們先定義變更背景圖片方式,因為直接改變圖片會顯得有點生硬,所以我們也同時變更他的透明度,看起來比較不衝突一些:

func changeIntroduceImage(number:Int) {  UIView.animate(withDuration: 0.4, animations: {    self.introduceImage.alpha = 0  }) { (finish) in    UIView.animate(withDuration: 0.4, animations: {     self.introduceImage.image = UIImage(named: "台灣常見貓咪介紹-\(self.imageArray[number])")
self.titleLabel.text = "台灣常見貓咪介紹-\(self.imageArray[number])"
self.introduceImage.alpha = 1
}) }}

所以我們的圖片會先由原有的圖片做一個淡出效果 ,接著淡入一張新的圖片,接下來我們要做下方按鈕的位移效果,我們這邊貓咪的按鈕都是放在一個 Outlet Collection 中,所以我們都使用 for in 語法來調用所有按鈕:

@IBAction func moveStackView(_ sender: UIButton) {  if sender.tag == currentNum {     return  }  // 用按鈕的 tag 來判斷是否跟上一個按鈕相同
currentNum = sender.tag
// 計算該按鈕與畫面中心距離
let moveX = self.catStackView.center.x - sender.center.x
UIView.animate(withDuration: 0.2, animations: { for button in self.catButtons { button.center.x += moveX } }) { (finish) in UIView.animate(withDuration: 0.2, animations: { // 將所有 Button 恢復原狀
for button in self.catButtons {
button.transform = CGAffineTransform.identity } // 更換圖片
self.changeIntroduceImage(number: sender.tag)
// 放大點選的 Button
sender.transform = CGAffineTransform(scaleX: 1.5, y: 1.5)
}) }}
實際 Demo 效果畫面

# Transformation(變形)

其實已經有很多關於 Transformation 的 UX 動畫原理文章。某種程度上來說,這是最顯眼也最廣泛運用的動畫原理。Transformation 之所以最具有代表性,大多是因為它特別吸引目光。

因為變形就是很炫很潮的效果!!

所以我們常常會看到很新潮的變形效果,藉由變形來告訴使用者可能這個物件在運作什麼事情,例如下面的變形範例:

Submit 變形效果
Send 變形效果

由上面兩個範例你可以發現,兩個動畫效果的按鈕都從一個 Button 變成一個進度條,也是藉由這種效果告訴使用者可能目前正在「 開始讀取或是下載資訊 」,加上呈現的效果也是十分吸睛,讓使用者可能除了知道目前在下載檔案,也被這個動畫效果吸引,所以下面我也實作一個按鈕變形進度條的效果。

DIY 變形效果按鈕原樣

其實實作變形效果不難,只是要記得他目前改變到了哪裡,接下來要變成什麼,所以他的效果可能是一層一層實現的,我們就來看程式碼說故事了,這邊因為直接在 Medium 貼上程式碼可能難以分解,所以我用圖片講解:

變形動畫效果程式碼

首先我們想像一個 UIView.animate 為一層的動畫執行過程,因此我們一層層來說明他做了什麼變化:

# 第一層

  • 變更按鈕文字為 “ OK ”
  • 縮小按鈕為寬高為 60 的正方形( 因為按鈕原本有圓角效果,所以這邊會變成圓形 )
  • 按鈕中心點為 View 的中心點
  • 改變按鈕背景顏色為白色

# 第二層

  • 使用空字串( “” )來清除按鈕文字
  • 將按鈕圓角效果取消,變成正方形
  • 使按鈕旋轉 180 度
  • 使按鈕恢復原樣( 此時會在旋轉回來 )

# 第三層

  • 將按鈕背景顏色設為透明
  • 將按鈕變回寬為 180,高為 60 的矩形
  • 按鈕中心點為 View 的中心點
  • 變更按鈕文字為 “ Loading ”

接著你會發現我們執行完了,接著一個 loadAnimation 的方法,是因為我們不想繼續接續在這個方法裡面,不然這個方法可能會被我們寫的很長,因此我們讓他跳到其他方法中繼續執行動畫:

loadingAnimation 方法

這邊我們先生成一個 x、y 位置與我們按鈕相同的 View,來實現進度條效果,並且我們只設定他的高度,因為沒有設置他的高度的話,他等等執行動畫時候會從左上角開始動畫,因為我們要做的效果是進度條效果,因此要設定起始高度,接著我們繼續分層解析:

# 第一層

  • 把我們的所生成的設為我們 button 的 frame

# 第二層

  • 變更我們按鈕文字為 Complete
  • 變更按鈕背景顏色
  • 恢復我們的圓角笑果
  • 將剛剛生成的 View 移除

讓我們看看我們做出的成品吧,我們這邊先演示沒有設定起始高度的動畫:

沒有設定起始高度的動畫效果

所以你就很明顯地知道這不是我們想要的效果,所以需要設一個起始高度:

按鈕變形讀取條動畫效果

那我們第二期的 UX in motion 的動畫實作教學到這邊,我們工作室的活動 「 想知道嗎 」 我的分享也在昨天結束了,就目前看起來可能還需要兩次我才能把所有動畫原理實作講完( 跌入大坑 )。

之後我會分享一些我自己在實作動畫的小技巧,等我好好整理之後分享成文章讓大家知道,大概就兩篇文章一次小文章分享,跟「想知道嗎」週期相同同,一個禮拜一次。

下面附上這兩次文章、四種動畫效果的 Demo:

順帶一提,這個 Demo 我 Transformation 裡面有做一個解鎖效果的動畫,因為是硬幹出來的,所以我在這邊文章沒有提到,等我之後能夠詳細實作再分享給大家。

--

--

Jeremy Xue
Jeremy Xue

Written by Jeremy Xue

Hi, I’m Jeremy. [好想工作室 — iOS Developer]

No responses yet