LoginSignup
2
0

【SwiftUI】非同期で画像を読み込む方法 - AsyncImageを用いて -

Last updated at Posted at 2023-05-05

SwiftUIで、非同期で画像を読み込む際に、詰まったので記事として記しておく。
初学者記事のため、疑いながら読んでください。

SwiftUIで下画像のような画面を、APIを用いて表示する際に、
スクリーンショット 2023-05-05 7.09.07.png

推奨しないサンプルコード
PokemonImageView.swift
import SwiftUI

struct PokemonImageView: View {
    let imageUrl: URL?
    
    var body: some View {
        if let imageUrl = imageUrl,
           let imageData = try? Data(contentsOf: imageUrl) {
            return Image(uiImage: UIImage(data: imageData)!)
                .resizable()
                .aspectRatio(contentMode: .fit)
        } else {
            return Image(systemName: "questionmark.circle")
                .resizable()
                .aspectRatio(contentMode: .fit)
        }
    }
}

推奨しないサンプルコードで、実装した。

シュミレーターで起動した際に、
スクリーンショット 2023-05-05 7.08.03.png

紫の警告が表示され、詳しく見てみると、

Synchronous URL loading of https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/1.png should not occur on this application's main thread as it may lead to UI unresponsiveness. Please switch to an asynchronous networking API such as URLSession.

この警告は、メインスレッドで同期的にURLを読み込んでいることによって、ユーザーインターフェースが応答しなくなる可能性があるという意味です。丁寧に該当するコードの部分で、「非同期で行った方が良いよ」と注意してくれている。

対処法

非同期で行うためには、AsyncImageを用いることが必要です。

実装例

サンプルコード
PokemonImageView
struct PokemonImageView: View {
    let imageUrl: URL?
    
    var body: some View {
        if let imageUrl = imageUrl {
            AsyncImage(url: imageUrl) { phase in
                switch phase {
                case .empty:
                    Image(systemName: "questionmark.circle")
                        .resizable()
                        .aspectRatio(contentMode: .fit)
                case .success(let image):
                    image
                        .resizable()
                        .aspectRatio(contentMode: .fit)
                case .failure:
                    Image(systemName: "xmark.circle")
                        .resizable()
                        .aspectRatio(contentMode: .fit)
                @unknown default:
                    fatalError()
                }
            }
        } else {
            Image(systemName: "questionmark.circle")
                .resizable()
                .aspectRatio(contentMode: .fit)
        }
    }
}

実装例の詳細

AsyncImage(url: URL(string: "https://example.com/icon.png"))

上記のコードを、bodyで実装するだけでも、使用することは可能である。だが、上記のURLで画像取得に失敗した場合に、下画像の赤枠のような表示になり、読み込んでいるのかどうなのかが不明になってしまう。
スクリーンショット 2023-05-05 7.28.44.png

そのため、エラーに対して表示内容を変更する必要がある。そこで、AsyncImagePhaseを用いる。
スクリーンショット 2023-05-05 7.39.57.png

AsyncImagePahse(列挙型)の詳細を見ていく。
スクリーンショット 2023-05-05 7.44.15.png
今回使用したのは、

empty まだ画像を読み込んでいない
success 画像の読み込みが成功したとき、取得したImageを使用可能
failure 画像の読み込みに失敗したとき、取得したErrorを使用可能

である。

これらを用いて、条件に合ったUIを表示する。

以上です。

他にも良い方法があれば、コメントいただけると大変うれしいです。
良かったと思ったら、いいねやTwitterのフォローよろしくお願いいたします!

https://sites.google.com/view/muranakar
個人でアプリを作成しているので、良かったら覗いてみてください!

参考

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0