【SwiftUI】CGAnimateImageDataWithBlockやCGAnimateImageAtURLWithBlockを使ってGIFを表示する

# はじめに
数日前に「【SwiftUI】GIFを表示したい」という記事を書きました。

https://qiita.com/SNQ-2001/items/f242f194391a47910e86

コメント欄でもっといい表示方法を教わったので試してみました。

# サンプルアプリ
![Simulator Screen Recording - iPhone 15 - 2024-02-08 at 22.24.03.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/d1569305-61e7-7978-f028-d5b6bc3f0d32.gif)

# 今回使用するGIF
![heart.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/c5cc430a-6af6-986c-afc6-d102f4fd8991.gif)

<a href="https://pixabay.com/ja/users/supergsatb-32809140/?utm_source=link-attribution&utm_medium=referral&utm_campaign=animation&utm_content=10539">SuperGSATB</a>による<a href="https://pixabay.com/ja//?utm_source=link-attribution&utm_medium=referral&utm_campaign=animation&utm_content=10539">Pixabay</a>のGIF

# 実装
```swift
struct GIFView: View {
    @State private var image: Image?
    var gifData: CFData?
    var gifURL: CFURL?
    
    init(asset: NSDataAsset) {
        self.gifData = asset.data as CFData
        self.gifURL = nil
    }
    
    init(url: URL) {
        self.gifData = nil
        self.gifURL = url as CFURL
    }
    
    var body: some View {
        Group {
            if let image {
                image
                    .resizable()
                    .scaledToFit()
            } else {
                Text("読み込み失敗")
            }
        }
        .onAppear {
            if let gifData {
                CGAnimateImageDataWithBlock(gifData, nil) { index, cgImage, stop in
                    self.image = Image(uiImage: .init(cgImage: cgImage))
                }
            }
            if let gifURL {
                CGAnimateImageAtURLWithBlock(gifURL, nil) { index, cgImage, stop in
                    self.image = Image(uiImage: .init(cgImage: cgImage))
                }
            }
        }
    }
}
```

# GIFの読み込み
GIFの読み込み方が2種類あります。

### パターン1
Assetsの中にGIFを追加する方法です。

![スクリーンショット 2024-02-08 22.29.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/6742e533-d734-4aee-4d2c-68e3627a9329.png)


### パターン2
プロジェクトのディレクトリ内に追加する方法です。
![スクリーンショット 2024-02-08 22.30.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/90a4e5f9-e453-99e8-2baf-5fc2d7cb0822.png)

![スクリーンショット 2024-02-08 22.30.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/cc45deff-fb26-a756-3930-8c37065d1992.png)

# 実装
```swift
import SwiftUI

struct ContentView: View {
    var body: some View {
        VStack {
            // パターン1
            GIFView(asset: NSDataAsset(name: "heart")!)
                .frame(width: 300, height: 300)
            
            // パターン2
            GIFView(url: Bundle.main.url(forResource: "heart", withExtension: "gif")!)
                .frame(width: 300, height: 300)
        }
    }
}
```

# おわり
こんな機能があったなんて知りませんでした。
めっちゃ便利ですね！！

@Yajiro さんに感謝です！

# 参考記事
https://medium.com/%E5%BD%BC%E5%BE%97%E6%BD%98%E7%9A%84-swift-ios-app-%E9%96%8B%E7%99%BC%E5%95%8F%E9%A1%8C%E8%A7%A3%E7%AD%94%E9%9B%86/%E5%88%A9%E7%94%A8-cganimateimageaturlwithblock-%E6%92%AD%E6%94%BE-gif-4780071b835e

# 公式ドキュメント
https://developer.apple.com/documentation/imageio/3333272-cganimateimagedatawithblock

https://developer.apple.com/documentation/imageio/3333271-cganimateimageaturlwithblock