SwiftUI - 在多个视图中触发错误弹出窗口

SwiftUI - Error popup fired in multiple views

提问人:OuSS 提问时间:3/27/2023 更新时间:3/28/2023 访问量:71

问:

我尝试在我的项目中使用 MV 架构,下面是我的代码的简单示例:

struct PaymentView: View {
    @StateObject private var store = PaymentStore()
    
    var body: some View {
        NavigationStack {
            PaymentCreditorListView()
            /* -> PaymentFormView() */
            /* -> PaymentUnpaidView() */
            /* -> PaymentConfirmationView() */
        }
        .environmentObject(store)
    }
}

class PaymentStore: ObservableObject {
     ....
    @Published var isLoading = false
    @Published var popup: Popup?
    
    private let service: PaymentService
    
    init(service: PaymentService = PaymentService()) {
        self.service = service
    }
    
    func getPaymentCreditors() async {
        do {
            isLoading = true
            let response = try await service.fetchPaymentCreditors()
            .....
            isLoading = false
        } catch {
            isLoading = false
            popup = .init(error: error)
        }
    }
    
    func getPaymentForm() async {
        do {
            isLoading = true
            let response = try await service.fetchPaymentForm()
            ....
            isLoading = false
        } catch {
            isLoading = false
            popup = .init(error: error)
        }
    }
    
    func getPaymentUnpaid() async {
        do {
            isLoading = true
            let response = try await service.fetchPaymentUnpaid()
            .....
            isLoading = false
        } catch {
            isLoading = false
            popup = .init(error: error)
        }
    }
}

在每个视图上,我使用工作表来显示弹出错误,因为有时我需要为该视图做一些特定的事情(例如:调用 Web 服务或重定向等......

.sheet(item: $store.popup) { popup in
    PopupView(popup: popup) 
}

我现在遇到的唯一问题是,当其中一个端点返回错误时,所有使用弹出窗口的视图都会被触发,并且在控制台中收到此警告消息“尝试在已经呈现...的*上显示*”,progressLoader也有同样的问题,它将触发所有其他视图。

我错过了这种方法吗?或者有更好的方法吗?

iOS SwiftUI 错误处理 ObservableObject

评论

0赞 Paulw11 3/27/2023
是的。您已将弹出窗口添加到所有视图,并且它们都绑定到同一属性。只需将弹出窗口添加到根视图即可。
0赞 OuSS 3/27/2023
@Paulw11我不能这样做,因为通常我必须在用户确认错误后为每个视图做一些特定的事情
0赞 Paulw11 3/27/2023
然后,您将需要为每个视图提供特定的弹出绑定,而不仅仅是一个视图。
0赞 OuSS 3/27/2023
这就是我正在考虑要做的,谢谢

答:

0赞 lorem ipsum 3/27/2023 #1

您可以为每个 .@StateView

首先删除

@Published var popup: Popup?

并添加到您的 sView

@State private var popup: Popup?

然后在函数中,您需要类似的东西

func getPaymentCreditors(popup: Binding<Popup?>) async {
    do {
        isLoading = true
        let response = try await service.fetchPaymentCreditors()
        .....
        isLoading = false
    } catch {
        isLoading = false
        popup.wrappedValue = .init(error: error)
    }
}

工作表将引用局部变量

.sheet(item: $popup) { popup in
0赞 John Appleseed 3/28/2023 #2

这是因为您同时调用所有三个 API,并显示所有三个 API 的错误。您需要检查是否显示错误

if popup != nil {
   popup = .init(error: error)
}

当然,如果你没有任何其他代码试图显示一些东西,这取决于你如何使用和其他方法。更新时,更新所有内部视图,因为您将其用作环境对象。但是,如果您不使用子视图(例如弹出窗口)中的任何更新字段,则不必担心 - 在这种情况下,子视图将更新 UI。仅当子视图依赖于更新的字段时,它才会更新 UI。getPaymentCreditors()popupPaymentStore