有人可以向我解释为什么这在 SwiftUI 2.0 / iOS 14 / Xcode 12 中不起作用以及有效的解决方法吗? AlertOne 工作正常,但 AlertTwo 不行。我正在尝试对父视图和子视图(嵌套)发出警报。如果需要,您可以将其插入 Xcode 进行测试。谢谢你。
import SwiftUI
struct ContentView: View {
@State private var alertOne = false
var body: some View {
VStack {
Button("Alert One") {
self.alertOne.toggle()
}
TestView().padding()
}
.alert(isPresented: $alertOne) {
Alert(title: Text("Alert One"), dismissButton: .default(Text("Got it!")))
}
}
}
struct TestView: View {
@State private var alertTwo = false
var body: some View {
Button("Alert Two") {
self.alertTwo.toggle()
}
.alert(isPresented: $alertTwo) {
Alert(title: Text("Alert Two"), dismissButton: .default(Text("Got it!")))
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
这是构建代码的一种可能方法:
struct ContentView: View {
@State private var showAlert = false
@State private var alert: Alert? = nil
var body: some View {
VStack {
Button("Alert One") {
alert = Alert(title: Text("Alert One"), dismissButton: .default(Text("Got it!")))
self.showAlert.toggle()
}
TestView(showAlert: $showAlert, alert: $alert).padding()
}
.alert(isPresented: $showAlert) {
alert!
}
}
}
struct TestView: View {
@Binding var showAlert: Bool
@Binding var alert: Alert?
var body: some View {
Button("Alert Two") {
alert = Alert(title: Text("Alert Two"), dismissButton: .default(Text("Got it!")))
self.showAlert.toggle()
}
}
}
这是使用新类型
IdentifiableAlert
的替代方案。 这使您可以摆脱单独的 showAlert
并在将 IdentfiableAlert
分配给 alert
时显示警报:
struct IdentifiableAlert: Identifiable {
let alert: Alert
let id = UUID()
}
struct ContentView: View {
@State private var alert: IdentifiableAlert? = nil
var body: some View {
VStack {
Button("Alert One") {
alert = IdentifiableAlert(alert: Alert(title: Text("Alert One"), dismissButton: .default(Text("Got it!"))))
}
TestView(alert: $alert).padding()
}
.alert(item: $alert) { alert in
alert.alert
}
}
}
struct TestView: View {
@Binding var alert: IdentifiableAlert?
var body: some View {
Button("Alert Two") {
alert = IdentifiableAlert(alert: Alert(title: Text("Alert Two"), dismissButton: .default(Text("Got it!"))))
}
}
}
这是一个方便的扩展,可以将
Alert
变成 IdentifiableAlert()
:
extension Alert {
func identifiable() -> IdentifiableAlert { IdentifiableAlert(alert: self) }
}
所以这个:
alert = IdentifiableAlert(alert: Alert(title: Text("Alert Two"), dismissButton: .default(Text("Got it!"))))
可以用这个代替:
alert = Alert(title: Text("Alert Two"), dismissButton: .default(Text("Got it!"))).identifiable()
我认为这是一个错误,但实际上 SwiftUI 的行为方式是这样的 - 它不允许在一个视图层次结构中存在多个工作表或警报(因为我假设它们在内部使用视图首选项,所以一旦在一个周期中设置它就不会更新) .
可能的解决方案是使用一个
.alert
作为视图层次结构并从不同部分配置它,或者使用辅助托管视图人为地分离视图层次结构。
使用 Xcode 12 / iOS 14 进行测试
代码中唯一的变化 // ...
// you can wrap any part at any level to make it independent
// on existing view hierarchy !!
HelperView { TestView().padding() }.fixedSize()
// ...
和辅助视图
struct HelperView<Content: View>: UIViewRepresentable {
let content: () -> Content
func makeUIView(context: Context) -> UIView {
let controller = UIHostingController(rootView: content())
return controller.view
}
func updateUIView(_ uiView: UIView, context: Context) {}
}
同一视图上的多个警报不起作用,除非您将它们放在不同的视图上。 这是一种通过添加空视图作为背景来允许同一视图上出现多个警报的解决方法。
extension View {
@ViewBuilder
public func alertOnBackground(isPresented: Binding<Bool>, alert: @escaping () -> Alert) -> some View {
background(
EmptyView()
.alert(isPresented: isPresented, content: alert)
)
}
}
将其添加为您的(父)视图上的 viewModifier,例如:
.alertOnBackground(isPresented: $showAlertBinding) {
Alert(
title: "some title"),
message: Text("some message"),
dismissButton: .default(Text("ok"), action: {
// your action
})
)
}
我认为最快捷的方法是像这样使用
@Binding
:
import SwiftUI
struct ParentAlert: View {
@State private var showAlert: Bool = false
@State private var alertText: String = ""
private func handleSubmit() {
print("submit")
}
var body: some View {
VStack {
Text("Parent View")
Button("Alert One") {
self.alertText = "Alert One"
self.showAlert.toggle()
}
TestView(showAlert: $showAlert, alertText: $alertText)
}.alert(isPresented: $showAlert) {
Alert(title: Text("Are You Sure?"),
message: Text("Alert Text Is: \(self.alertText)"),
primaryButton: .destructive(Text("Save")) {
self.handleSubmit()
}, secondaryButton: .cancel()
)
}
}
}
struct TestView: View {
@Binding var showAlert: Bool
@Binding var alertText: String
var body: some View {
VStack {
Text("TestView / Child")
Button("Alert Two") {
self.alertText = "Alert Two"
self.showAlert.toggle()
}
}
}
}
#Preview {
ParentAlert()
}