首页 > 解决方案 > 为什么显示延迟警报?

问题描述

场景:
我正在尝试访问和显示远程图像以了解 Combine 的各种通知标志/协议。

一个目标:
访问错误的 URL 应立即显示 Alert()。

现实:
警报显示在第二个请求之后及之后。

在此处输入图像描述

这是主要(调用)视图:

import Combine
import SwiftUI

struct ContentView: View {
    @EnvironmentObject var settings: MySettings
    @State private var url: String = "https://garbage.com"  // ...purposely set to display alert.
    @State private var image: URLImage = URLImage()
    @State private var angelFish: Image = Image("QueenAngelfish")
    @State private var isPresented = false

    var body: some View {
        ZStack {
            Color.green
            NavigationView {
                VStack {
                    Button(action: {
                        self.url = "garbage.com"
                        self.isPresented = self.image.imageLoader.isPresented
                        self.image.imageLoader.load(url: URL(string: self.url)!)
                    }) {
                        Text("Get An Image")
                    }

                    angelFish
                        .resizable()
                        .frame(width: 300, height: 200, alignment: .center)
                        .padding()
                    image
                        .alert(isPresented: $isPresented, content: { () -> Alert in
                            Alert(title: Text(verbatim: "Unable to Acquire Image."))
                        })

                }.navigationBarTitle(Text(settings.name))
            }
        }
    }
}

这是访问引擎:

import Combine
import SwiftUI

enum ImageURLError: Error {
    case dataIsNotAnImage
}

struct URLImage: View {
    @EnvironmentObject var settings: MySettings
    @ObservedObject var imageLoader: ImageLoader
    var placeholder: Image

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

    var body: some View {
        VStack {
            imageLoader.image == nil ?
                placeholder : Image(uiImage: imageLoader.image!)
            Button(action: {
                self.settings.name = "Happy Thanks Giving"
                self.settings.isPresented = true
            }, label: {
                Text("Touch Me")
            })
        }
    }
}

// =====================================================================================================

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

    @Published var isPresented = false

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

    // ---------------------------------------------------------------------------

    func load(url: URL) {
        print("Hello Ric: ", #function)

        URLSession.shared.dataTask(with: url) { data, _, error in
            DispatchQueue.main.async {
                if error != nil {
                    self.isPresented = true
                    self.didChange.send()   // ...attempting to activate alert().
                    return
                }

                self.image = UIImage(data: data!)
            }

        }.resume()
    }
}

由于 URL 错误,此代码故意创建错误。
我试图通过将布尔“isPresented”标志作为“@Published”变量传递来立即通知调用例程

预期结果:
显示警报。

实际结果:
在初始尝试之后及之后显示警报。

问题:如何执行即时警报显示?

标签: alertswiftuicombine

解决方案


好的,这里有几个问题,主要是关于 ObservableObject 的使用和将事物链接在一起。请在下面找到有效的修改模块。(我用我的替换了一些不存在的实体只是为了测试)。

import Combine
import SwiftUI

class ImageLoader: ObservableObject {
    let id: String = UUID().uuidString

    // !!! no need in didChange, @Published is already Publisher
    // see below .onReceive for usage example
    @Published var isPresented = false
    @Published var image: UIImage?

    func load(url: URL) {
        URLSession.shared.dataTask(with: url) { data, _, error in
            DispatchQueue.main.async {
                if error != nil {
                    self.isPresented = true
                    return
                }
                self.image = UIImage(data: data!)
            }
        }.resume()
    }
}

class MySettings: ObservableObject { // << reconstructed for testing
    @Published var name = "My Name"
    @Published var isPresented = false
}

enum ImageURLError: Error {
    case dataIsNotAnImage
}

struct URLImage: View {
    @EnvironmentObject var settings: MySettings
    @ObservedObject var imageLoader: ImageLoader
    var placeholder: Image

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

    var body: some View {
        VStack {
            imageLoader.image == nil ?
                placeholder : Image(uiImage: imageLoader.image!)
            Button(action: {
                self.settings.name = "Happy Thanks Giving"
                self.settings.isPresented = true
            }, label: {
                Text("Touch Me")
            })
        }
        .onReceive(imageLoader.$isPresented) { self.settings.isPresented = $0 }
    }
}

struct ContentView: View {
    @EnvironmentObject var settings: MySettings
    @State private var url: String = "https://garbage.com"  // << this url is valid
    @State private var image: URLImage = URLImage()
    @State private var angelFish: Image = Image("QueenAngelfish")

    var body: some View {
        ZStack {
            Color.green
            NavigationView {
                VStack {
                    Button(action: {
                        self.url = "garbage.com" // << this url is not valid [to test alert]
                        self.image.imageLoader.load(url: URL(string: self.url)!)
                    }) {
                        Text("Get An Image")
                    }

                    angelFish
                        .resizable()
                        .frame(width: 300, height: 200, alignment: .center)
                        .padding()
                    image
                        .alert(isPresented: $settings.isPresented) {
                            Alert(title: Text(verbatim: "Unable to Acquire Image."))
                        }
                }.navigationBarTitle(Text(settings.name))
            }
        }
    }
}

struct TestDelayedAlert_Previews: PreviewProvider {
    static var previews: some View {
        ContentView().environmentObject(MySettings())
    }
}

推荐阅读