前篇提到,業界專業的 App 如果有 UIUX 設計師,會使用 Lottie 產出輕量的 JSON 檔,並套用 Lottie SDK 跟封裝好的 SwiftUI LottieView 去顯示,但是在 LeetCode 呈現動畫上,我們要看的是資料的變化,而不是精美的圖像,所以評估上學習太專業的動畫軟體不符合時間成本,因此簡單製作、教學資源豐富的 GIF 檔就變成首選。
首先會建置 UIImage
的擴充程式碼,因為 GIF 圖片沒有現成的 View 可以直接讀取,所以我們寫法會比較像是拼湊起來的。
extension UIImage {
public class func gif(data: Data) -> UIImage? {
guard let source = CGImageSourceCreateWithData(data as CFData, nil) else {
print("SwiftGif: Source for the image does not exist")
return nil
}
return UIImage.animatedImageWithSource(source)
}
class func animatedImageWithSource(_ source: CGImageSource) -> UIImage? {
let count = CGImageSourceGetCount(source)
var images = [CGImage]()
var duration = 0.0
for i in 0..<count {
if let image = CGImageSourceCreateImageAtIndex(source, i, nil) {
images.append(image)
duration += UIImage.frameDurationAtIndex(source, index: i)
}
}
return UIImage.animatedImage(with: images, duration: duration)
}
class func frameDurationAtIndex(_ source: CGImageSource, index: Int) -> Double {
var frameDuration: Double = 0.1
let cfFrameProperties = CGImageSourceCopyPropertiesAtIndex(source, index, nil)
let frameProperties = cfFrameProperties as NSDictionary?
let gifProperties = frameProperties?[kCGImagePropertyGIFDictionary] as? NSDictionary
if let delayTimeUnclampedProp = gifProperties?[kCGImagePropertyGIFUnclampedDelayTime] as? NSNumber {
frameDuration = delayTimeUnclampedProp.doubleValue
} else if let delayTimeProp = gifProperties?[kCGImagePropertyGIFDelayTime] as? NSNumber {
frameDuration = delayTimeProp.doubleValue
}
if frameDuration < 0.011 {
frameDuration = 0.1
}
return frameDuration
}
}
再來我們就可以直接製作 GIF SwiftUI 長甚麼樣子。
import SwiftUI
struct GIFView: View {
let gifName: String
var body: some View {
if let url = Bundle.main.url(forResource: gifName, withExtension: "gif"),
let data = try? Data(contentsOf: url),
let uiImage = UIImage.gif(data: data) {
return Image(uiImage: uiImage)
.resizable()
.scaledToFit()
} else {
return Text("Unable to load GIF")
}
}
}
前提是請確保在 Assets
中新增我們需要的 GIF 圖片,並將其名稱作為參數傳遞給 GIFView
最後就可以在主要的 View 直接調用。
struct ContentView: View {
var body: some View {
GIFView(gifName: "example")
.frame(width: 200, height: 200)
}
}
基本上這個做法還是蠻原始的,如果不想重複造輪子,也可以參考第三方的現成函示庫,這樣就不用擔心邏輯要重新寫,別人會幫你處理好,除非第三方作者不維護了。
有查到一個 Github 專案是 swiftui-gif,那麼就會是考量範圍了,但是還是要留意,可以往下看評估第三方的考慮點。
如何知道第三方函示庫是我們可以安心使用的呢?筆者有準備以下幾招可以給大家參考:
沒有最完美無瑕的第三方工具,只有適合專案自己使用的場景,這就需要仰賴工程師的專業評估,在這個案例中,其實找了不少搜尋專案結果,發現 GIF 圖片真的偏冷門,所以在實作上還是偏傳統做法去轉換 SwiftUI 邏輯,但願未來 Apple 官方可以出一個專屬動畫圖片呈現的 SwiftUI 的 View,這樣就天下太平,大家也不用瘋狂踩坑而在坑底出不來。