iT邦幫忙

2023 iThome 鐵人賽

DAY 21
0
Mobile Development

用 SwiftUI 魔法變出 Leetcode 刷題知識學習 App!系列 第 21

Day 21: SwiftUI 用 GIF 圖片動畫播放任何 LeetCode 演算法

  • 分享至 

  • xImage
  •  

前篇提到,業界專業的 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,那麼就會是考量範圍了,但是還是要留意,可以往下看評估第三方的考慮點。

評估第三方

如何知道第三方函示庫是我們可以安心使用的呢?筆者有準備以下幾招可以給大家參考:

  1. 收藏評星數:這個表示越來越多人覺得好用,那麼就可以考慮導入
  2. Issue 數量:這個數量越多表示問題越多,可以仔細去看看別人的發問是不是跟你一樣,這樣就不必踩坑。
  3. 社群活躍度:越活躍表示越多人關心,那麼討論聲量比較大表示你遇到的問題別人也會遇到,更容易獲得解答。

總結

沒有最完美無瑕的第三方工具,只有適合專案自己使用的場景,這就需要仰賴工程師的專業評估,在這個案例中,其實找了不少搜尋專案結果,發現 GIF 圖片真的偏冷門,所以在實作上還是偏傳統做法去轉換 SwiftUI 邏輯,但願未來 Apple 官方可以出一個專屬動畫圖片呈現的 SwiftUI 的 View,這樣就天下太平,大家也不用瘋狂踩坑而在坑底出不來。


上一篇
Day 20: 導讀 LeetCode 演算法 - Tree 的 DFS 與 BFS (Swift)
下一篇
Day 22: 導讀 LeetCode 演算法- Binary Search (Swift)
系列文
用 SwiftUI 魔法變出 Leetcode 刷題知識學習 App!30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言