【问题标题】:How to Show Alert from Anywhere in app SwiftUI?如何在应用程序 SwiftUI 中从任何地方显示警报?
【发布时间】:2021-08-06 14:13:13
【问题描述】:

我有条件在可以从应用程序的任何位置显示的视图中显示警报。就像我想从根视图中呈现它一样,它可能会显示在所有视图中。目前,当我从第一个视图呈现时会发生什么,它将显示该警报,直到我流动相同的导航视图。一旦任何工作表打开警报都不会显示在其上。在 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"))
        })
    }
}

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

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

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

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>.

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

【问题讨论】:

  • 您正尝试在 viewA 上显示警报,而 viewB 已经出现。这听起来不正确,你到底想要什么?您可以只使用 contentView 中的 Bool 值,并根据 bool 状态在按钮单击时在 viewB 中发出单独的警报。
  • @TusharSharma 感谢您的回复。实际上我的条件是我想从一个地方在整个应用程序中显示警报。假设我的应用程序中有 n 个视图,还有可用的模型表。如果我在一个模型表中,并且在应用程序的第一个视图中我的警报。如果当我在工作表中时它会跳动。我想显示那个警报。希望您了解我的情况。
  • @TusharSharma,实际上我想找到可以从任何地方显示警报的应用程序显示的根视图。目前在上述情况下,工作表遵循不同的视图层次结构,而 ContentView 遵循不同的层次结构,因此警报不会显示在工作表上。
  • 所以您的警报触发器将从根视图触发,并且您希望将此警报显示到任何视图?
  • @RajaKishan,目前在我的应用程序中,我在 WindowGroup {} 中我的应用程序的第一个视图中添加了此警报。它可以在应用程序中使用,但是一旦在当前状态下打开任何工作表,就不会显示警报。每当该警报触发时,我想在应用程序中甚至在工作表中显示该警报。

标签: ios swift xcode swiftui


【解决方案1】:

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

  1. 创建通知顶层并要求显示警报的Alerter
class Alerter: ObservableObject {
    @Published var alert: Alert? {
        didSet { isShowingAlert = alert != nil }
    }
    @Published var isShowingAlert = false
}
  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(""))
                }
        }
    }
}
  1. 设置应从子视图内部显示的警报
struct SomeChildView: View {

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

关于工作表的注释

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

【讨论】:

    【解决方案2】:

    这是一个可能的示例解决方案,可在应用程序的任何位置显示警报。 它使用“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")
                }
            }
        }
    }
    

    【讨论】:

    • 这已经在我的应用程序中实现了,但是当您尝试在任何工作表中触发警报时,这种情况也会失败。
    • 工作表是模态的,所以据我所知,您必须在其中添加一个单独的警报才能使其正常工作。
    猜你喜欢
    • 2021-08-11
    • 1970-01-01
    • 2021-04-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-08-08
    相关资源
    最近更新 更多