以下のような、画像を取得するObservableObjectを @StateObject
で保持している画像表示のViewを作成することがままあると思います。
import SwiftUI
struct ImageView: View {
@StateObject var imageProvider = ImageProvider()
let url: URL
var body: some View {
ZStack {
if let image = imageProvider.image {
Image(uiImage: image)
.resizable()
.scaledToFit()
}
}
.frame(height: 300)
.onAppear {
imageProvider.fetchImage(url: url)
}
}
}
class ImageProvider: ObservableObject {
@Published var image: UIImage?
func fetchImage(url: URL) {
URLSession.shared.dataTask(with: url) { data, response, error in
if let data = data, let image = UIImage(data: data) {
DispatchQueue.main.async {
self.image = image
}
}
}.resume()
}
}
画像を一度だけ取得する場合は問題なのですが、以下のように任意の契機で別の画像を取得したい場合に期待通りに画像が更新されません。
import SwiftUI
struct ContentView: View {
@State var pictureNumber = 0
var body: some View {
ImageView(url: URL(string: "https://picsum.photos/id/\(pictureNumber)/500/500")!)
Button {
pictureNumber += 1
} label: {
Text("Change")
}
}
}
画像取得処理が確実に呼ばれるようにImageViewのイニシャライザー内に imageProvider.fetchImage(url: url)
を置いても特に改善しません。
struct ImageView: View {
@StateObject var imageProvider: ImageProvider
let url: URL
init(url: URL) {
self.url = url
let imageProvider = ImageProvider()
_imageProvider = StateObject(wrappedValue: imageProvider)
imageProvider.fetchImage(url: url)
}
...
結局以下のようにImageViewに .id()
を付与することで解決しました。
idが更新されると対象のViewの状態がリセットされるとのことです。
struct ContentView: View {
...
ImageView(url: URL(string: "https://picsum.photos/id/\(pictureNumber)/500/500")!)
.id(pictureNumber)
...
参考
環境
Xcode 14.1.0
Swift 5.7.1