沒有想到身為Android Developer的我, 居然天字第一號文章寫了篇iOS的小範例
起因來自於跟幾位想轉職成為軟體工程師的Appworks School學員聊到了Lottie, 也建議他們有空可以玩一玩, 於是乎我想了想乾脆來一寫一個我以前在Android上面也實現過, 簡單的動態變色功能來拋磚引玉吧😁
功能有哪些?
- 使用Lottie, 在首頁上以loop的方式輪播一個動畫
- 在首頁上放上一個按鈕, 讓使用者可以自由選擇顏色
- 以Dark mode開啟的話, 預設動畫跟按鈕皆為白色, 反之則為灰色
- 根據使用者選擇的顏色, 改變動畫以及按鈕字的顏色
接下來就開始一步步完成這個小程式吧
開一個新專案並引入Lottie
https://lottiefiles.com/blog/working-with-lottie/how-to-add-lottie-animation-ios-app-swift
去LottieFiles抓一個動畫
我範例裡面使用的是免費版的鑽石動畫 (https://lottiefiles.com/143952-diamond-back-to-basics)
UI設定
ColorPicker使用原生的 UIColorPickerViewController
放動畫跟按鈕到首頁上
// setup button
button.setTitle("Pick Diamond color", for: .normal)
button.addTarget(self, action: #selector(buttonAction), for: .touchUpInside)
self.view.addSubview(button)
// setup lottie animation view
animationView = .init(name: "animation")
animationView.contentMode = .scaleAspectFit
animationView.loopMode = .loop
animationView.animationSpeed = 1
self.view.addSubview(animationView)
設constraint, 範例中讓動畫佔滿畫面高度的一半, 並且將按鈕對齊動畫的下方中央
// add constraint
animationView.translatesAutoresizingMaskIntoConstraints = false
animationView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
animationView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor).isActive = true
animationView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor).isActive = true
animationView.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.5).isActive = true
button.translatesAutoresizingMaskIntoConstraints = false
button.topAnchor.constraint(equalTo: animationView.bottomAnchor).isActive = true
button.centerXAnchor.constraint(equalTo: animationView.centerXAnchor).isActive = true
別忘了我們的dark mode需求
// setup initial color of the animation and button
if self.traitCollection.userInterfaceStyle == .dark {
self.updateDiamondColor(.white)
self.button.setTitleColor(.white, for: .normal)
self.picker.selectedColor = .white
} else {
self.updateDiamondColor(.black)
self.button.setTitleColor(.black, for: .normal)
self.picker.selectedColor = .black
}
動態改變動畫顏色
我們這邊需要使用Lottie Value Provider (細節就不深入討論啦…), 這邊要處理的是變色, 所以我們可以使用Default的ColorValueProvider
新增一個LottieAnimationView的extension來協助我們處理換色
extension LottieAnimationView {
func setAnimationWithChangeColor(name: String, color: UIColor, paths: [String]) {
paths.forEach {
self.setValueProvider(ColorValueProvider(color.lottieColorValue), keypath: AnimationKeypath(keypath: $0))
}}
}
不過… 什麼是 AnimationKeypath
啊..?
可以看一下定義: https://airbnb.io/lottie/#/ios?id=animation-keypaths
AnimationKeypath
is an object that describes a keypath search for nodes in the animation JSON.
Lottie把動畫需要的相關元素, 都記錄在JSON裏面, 成為一個一個的node, 而keypath就是讓我們找到這些node的路徑, 找到那些node之後, 就可以透過Lottie的API, 達到換色的效果
這時心中又冒出了疑問: 但我哪知道這個動畫有哪些node, 還有這些node的keypath分別又是什麼啊?
莫驚慌, 官方有提供方法log出所有動畫成像所需的keypath
animationView.logHierarchyKeypaths()
不過數量會有點多, 範例的動畫你會在console看到類似這樣的log
Lottie: Logging Animation Keypaths
33
33.Transform
33.Transform.Rotation Y
33.Transform.Anchor Point
33.Transform.Position
33.Transform.Scale
33.Transform.Rotation Z
33.Transform.Opacity
33.Transform.Rotation
33.Transform.Rotation X
33.Pre-comp 1
...
...
所以我們要自己找出需要的, 這邊要處理的是顏色, 所以要找出Color nodes (可以使用grep
)
33.Pre-comp 1.hareket Outlines 3.Group 1.Stroke 1.Color
33.Pre-comp 1.hareket Outlines 2.Group 1.Stroke 1.Color
33.Pre-comp 1.hareket Outlines.Group 1.Stroke 1.Color
33.Pre-comp 1.ort Outlines.Group 1.Stroke 1.Color
...
最後指定這些keypath給ColorValueProvider
就大功告成囉!
可以一條一條指定, 或是使用keypath wildcard, 用法在官方範例中也有提到, 這邊示範的是 *
(star), 跟**
(double star) 的用法
// Represents the color node for every Stroke named "Stroke 1" in the animation.
func updateDiamondColor( _ color: UIColor) {
self.animationView.setAnimationWithChangeColor(
name: "animation",
color: color,
paths: [
"**.Stroke 1.Color"
]
)
}
// list all the color nodes we need
func updateDiamondColor( _ color: UIColor) {
self.animationView.setAnimationWithChangeColor(
name: "animation",
color: color,
paths: [
"33.Pre-comp 1.hareket Outlines *.Group 1.Stroke 1.Color",
"33.Pre-comp 1.hareket Outlines.Group 1.Stroke 1.Color",
"33.Pre-comp 1.ort Outlines.Group 1.Stroke 1.Color",
"33.Pre-comp 1.taban Outlines.Group *.Stroke 1.Color",
"prlt Outlines *.Group *.Stroke 1.Color"
]
)
}
最後來看一下成果吧
github: https://github.com/kash-yang/LottiePlayground
祝各位Coding愉快