如何在应用程序 SwiftUI 中从任何地方显示警报?

Shr*_*ava 11 xcode ios swift swiftui

我有条件在视图中显示警报,该视图可以从应用程序中的任何位置显示。就像我想从根视图中呈现它一样,这样它就可以在所有视图中显示。目前,当我从第一个视图呈现时,它将显示该警报,直到我流动相同的导航视图。一旦任何工作表打开,警报就不会显示在其上。SwiftUI 中有任何解决方案可以将警报从一处显示到整个应用程序。

这是我当前的代码实现。这是我的 contentView,其中显示工作表并在其中添加警报。

 struct ContentView: View {
    @State var showAlert: Bool = false
    @State var showSheet: Bool = false
    var body: some View {
        NavigationView {
            Button(action: {
                showSheet = true
            }, label: {
                Text("Show Sheet")
            }).padding()
            .sheet(isPresented: $showSheet, content: {
                SheetView(showAlert: $showAlert)
            })
        }
        .alert(isPresented: $showAlert, content: {
            Alert(title: Text("Alert"))
        })
    }
}
Run Code Online (Sandbox Code Playgroud)

在这里,我从工作表中切换警报,但不显示警报。

 struct SheetView: View {
    @Binding var showAlert: Bool
    var body: some View {
        Button(action: {
            showAlert = true
        }, label: {
            Text("Show Alert")
        })
    }
}
Run Code Online (Sandbox Code Playgroud)

这是我们切换按钮时调试时出现的错误

AlertDemo[14187:3947182] [Presentation] Attempt to present <SwiftUI.PlatformAlertController: 0x109009c00> on <_TtGC7SwiftUI19UIHostingControllerGVS_15ModifiedContentVS_7AnyViewVS_12RootModifier__: 0x103908b50> (from <_TtGC7SwiftUI19UIHostingControllerGVS_15ModifiedContentVS_7AnyViewVS_12RootModifier__: 0x103908b50>) which is already presenting <_TtGC7SwiftUI29PresentationHostingControllerVS_7AnyView_: 0x103d05f50>.
Run Code Online (Sandbox Code Playgroud)

SwiftUI 有什么解决方案吗?提前致谢。

Lin*_*rth 12

我能够通过@workingdog 在他们的答案中建议的简化版本来实现这一目标。其工作原理如下:

  1. 创建Alerter通知顶层并要求显示警报的类
class Alerter: ObservableObject {
    @Published var alert: Alert? {
        didSet { isShowingAlert = alert != nil }
    }
    @Published var isShowingAlert = false
}
Run Code Online (Sandbox Code Playgroud)
  1. 在最顶层呈现警报,例如在您的@main结构或ContentView
@main
struct MyApp: App {
    @StateObject var alerter: Alerter = Alerter()
    
    var body: some Scene {
        WindowGroup {
            ContentView()
                .environmentObject(alerter)
                .alert(isPresented: $alerter.isShowingAlert) {
                    alerter.alert ?? Alert(title: Text(""))
                }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)
  1. 设置应从子视图内部显示的警报
struct SomeChildView: View {

    @EnvironmentObject var alerter: Alerter
    
    var body: some View {
        Button("show alert") {
            alerter.alert = Alert(title: Text("Hello from SomeChildView!"))
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

床单上的注释

如果您将视图呈现为工作表,则每个工作表都需要实现自己的警报,就像MyApp上面一样。
如果您NavigationView的工作表内部有一个工作表,并在同一工作表中的导航视图中显示其他视图,则后续工作表可以使用第一个工作表的警报,就像SomeChildView我上面的示例中所做的那样。


wor*_*dog 1

这是一个可能的示例解决方案,可在应用程序中的任何位置显示警报。它使用“Environment”和“ObservableObject”。

import SwiftUI

@main
struct TestApp: App {
    @StateObject var alerter = Alerter()
    
    var body: some Scene {
        WindowGroup {
            ContentView().environment(\.alerterKey, alerter)
                .alert(isPresented: $alerter.showAlert) {
                    Alert(title: Text("This is the global alert"),
                          message: Text("... alert alert alert ..."),
                          dismissButton: .default(Text("OK")))
                }
        }
    }
}

struct AlerterKey: EnvironmentKey {
    static let defaultValue = Alerter()
}

extension EnvironmentValues {
    var alerterKey: Alerter {
        get { return self[AlerterKey] }
        set { self[AlerterKey] = newValue }
    }
}

class Alerter: ObservableObject {
    @Published var showAlert = false
}

struct ContentView: View {
    @Environment(\.alerterKey) var theAlerter
    var body: some View {
        NavigationView {
              VStack {
                  NavigationLink(destination: SecondView()) {
                      Text("Click for second view")
                  }.padding(20)
                Button(action: { theAlerter.showAlert.toggle()}) {
                    Text("Show alert here")
                }
              }
          }.navigationViewStyle(StackNavigationViewStyle())
    }
}

struct SecondView: View {
    @Environment(\.alerterKey) var theAlerter
    var body: some View {
        VStack {
            Button(action: { theAlerter.showAlert.toggle()}) {
                Text("Show alert in second view")
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 工作表是模态的,因此据我所知,您必须在其中添加一个单独的警报才能使其工作。 (3认同)