下载图像后在 SwiftUI 中更新图像视图

Updating image view in SwiftUI after downloading image

单击按钮后,我尝试下载新的随机图像并更新视图。当应用程序加载时,它会显示下载的图像。单击按钮时,图像似乎已下载,但视图永远不会更新并显示占位符图像。我在这里遗漏了什么,有什么想法吗?这是一个简化版本。

import SwiftUI

struct ContentView : View {

    @State var url = "https://robohash.org/random.png"

    var body: some View {
        VStack {
            Button(action: {
                self.url = "https://robohash.org/\(Int.random(in:0 ..< 10)).png"
            }) {
                Text("Get Random Robot Image")
            }
            URLImage(url: url)
        }

    }
}
class ImageLoader: BindableObject {

    var downloadedImage: UIImage?
    let didChange = PassthroughSubject<ImageLoader?, Never>()

    func load(url: String) {

        guard let imageUrl = URL(string: url) else {
            fatalError("Image URL is not correct")
        }

        URLSession.shared.dataTask(with: imageUrl) { data, response, error in
            guard let data = data, error == nil else {
                DispatchQueue.main.async {
                    self.didChange.send(nil)
                }
                return
            }

            self.downloadedImage = UIImage(data: data)
            DispatchQueue.main.async {
                print("downloaded image")
                self.didChange.send(self)
            }
        }.resume()
    }
}

import SwiftUI

struct URLImage : View {

    @ObjectBinding private var imageLoader = ImageLoader()
    var placeholder: Image

    init(url: String, placeholder: Image = Image(systemName: "photo")) {
        self.placeholder = placeholder
        self.imageLoader.load(url: url)
    }

    var body: some View {
        if let uiImage = self.imageLoader.downloadedImage {
            print("return downloaded image")
            return Image(uiImage: uiImage)
        } else {
            return placeholder
        }
    }
}

问题似乎与 ContentViewImageURL 之间的某种同步丢失有关(发生在按钮单击事件之后)。

一个可能的解决方法是使 ImageURL 成为 ContentView@State 属性。

之后,在按钮点击事件的范围内,我们可以调用image.imageLoader.load(url: )方法。图像下载结束时,发布者 (didChange) 将通知 ImageURL,然后更改会正确传播到 ContentView

import SwiftUI
import Combine

enum ImageURLError: Error {
    case dataIsNotAnImage
}

class ImageLoader: BindableObject {
    /*
    init(url: URL) {
        self.url = url
    }

    private let url: URL */

    let id: String = UUID().uuidString
    var didChange = PassthroughSubject<Void, Never>()

    var image: UIImage? {
        didSet {
            DispatchQueue.main.async {
                self.didChange.send()
            }
        }
    }

    func load(url: URL) {
        print(#function)
        self.image = nil

        URLSession.shared.dataTask(with: url) { (data, res, error) in
            guard error == nil else {
                return
            }

            guard
                let data = data,
                let image = UIImage(data: data)
            else {
                return
            }

            self.image = image
        }.resume()
    }

}

URLImage 视图:

struct URLImage : View {

    init() {
        self.placeholder = Image(systemName: "photo")
        self.imageLoader = ImageLoader()
    }

    @ObjectBinding var imageLoader: ImageLoader

    var placeholder: Image

    var body: some View {
        imageLoader.image == nil ?
            placeholder : Image(uiImage: imageLoader.image!)
    }
}

ContentView:

struct ContentView : View {

    @State var url: String = "https://robohash.org/random.png"
    @State var image: URLImage = URLImage()

    var body: some View {
        VStack {
            Button(action: {
                self.url = "https://robohash.org/\(Int.random(in: 0 ..< 10)).png"
                self.image.imageLoader.load(url: URL(string: self.url)!)
            }) {
                Text("Get Random Robot Image")
            }

            image
        }
    }
}

无论如何,我会尝试调查这个问题,如果我知道一些新的东西,我会修改我的答案。