【SwiftUI】AsyncImage で非同期に画像を表示する

Xcode13 & iOS15 より追加された AsyncImage を使って非同期に画像表示ができるようになりました

ちなみにこの記事を書いている2021/09/13時点ではまだ Xcode13 と iOS15 はベータ版となりますので試してみたい方はDeveloperサイトからベータ版Xcode13をダウンロードしてみて下さい。

また、iOS14以前で同様な機能を実装したい場合は下記の記事をご参考下さい

【SwiftUI】AsyncImage で非同期に画像を表示する

引数の確認

AsyncImage(url: URL?)
AsyncImage(url: URL?, content: (Image) -> View, placeholder: () -> View)
AsyncImage(url: URL?, content: (AsyncImagePhase) -> _)
AsyncImage(url: URL?, scale: CGFloat)
AsyncImage(url: URL?, scale: CGFloat, content: (Image) -> View, placeholder: () -> View)
AsyncImage(url: URL?, scale: CGFloat, transaction: Transaction, content: (AsyncImagePhase) -> _)
url: URL

参照先のURLを指定します

content: (Image) -> View

ダウンロード後に表示する Image の View を定義します

placeholder: View

ダウンロード中に表示しておく View(ProgressView など)を指定します

content: (AsyncImagePhase) -> View

ダウンロード状況に応じて表示を条件分岐させるときに使用します(※後述)。

scale: CGFloat

拡大率を指定するようですがあまり使われないと思います。

transaction: Transaction

こちらもあまり使われないと思います。Transaction の詳細についてはこちら

シンプルな使い方

struct ContentView: View {
    
    let url = URL(string: "https://〜.png")
    
    var body: some View {
        AsyncImage(url: url) { image in
            image.resizable().scaledToFit()
        } placeholder: {
            ProgressView()
        }
    }
}

ダウンロード中は placeholder: に ProgressView を表示し、ダウンロードが完了すると Image が渡されるのでそれをサイズ調整して表示しています。

Phase を使って状態をエラー処理をする

struct ContentView: View {
    
    let url = URL(string: "https://〜.png")
    
    var body: some View {
        AsyncImage(url: url) { phase in
            if let image = phase.image {
                image.resizable().scaledToFit()
            } else if let error = phase.error {
                Text(error.localizedDescription)
            } else {
                ProgressView()
            }
        }
    }
}

ダウンロード中は ProgressView を表示するのは変わりません。

ダウンロードが完了すると phase.image にデータが格納され何らかのエラーが発生すると phase.error にエラー内容が格納されます

エラー処理を考えるとこちらの方が実用的かと思います。

以上