SwiftUI模态展示仅从navigationBarItems中运作一次。

9

在SwiftUI中,当您从导航栏项内的按钮显示模态时存在一个错误。 在下面的代码中,Button 1按预期工作,但Button 2仅能工作一次:

struct DetailView: View {

    @Binding var isPresented: Bool
    @Environment (\.presentationMode) var presentationMode

    var body: some View {
        NavigationView {
            Text("OK")
            .navigationBarTitle("Details")
            .navigationBarItems(trailing: Button(action: {
                self.isPresented = false
                // or:
                // self.presentationMode.wrappedValue.dismiss()
            }) {
                Text("Done").bold()
            })
        }
    }
}

struct ContentView: View {

    @State var showSheetView = false

    var body: some View {
        NavigationView {
            Group {
                Text("Master")
                Button(action: { self.showSheetView.toggle() }) {
                    Text("Button 1")
                }
            }
            .navigationBarTitle("Main")
            .navigationBarItems(trailing: Button(action: {
                self.showSheetView.toggle()
            }) {
                Text("Button 2").bold()
            })
        }.sheet(isPresented: $showSheetView) {
            DetailView(isPresented: self.$showSheetView)
        }
    }
}

这个bug来自去年中期,仍存在于Xcode 11.3.1 + iOS 13.3模拟器和iOS 13.3.1 iPhone XS中。

有没有什么解决办法可以使按钮正常工作?

编辑:

  1. 似乎点击区域偏向下方,可能会点击按钮下方以显示模态框。

暂时的解决方法是使用内联导航栏模式:.navigationBarTitle(“Main”,displayMode:.inline)


一旦设备签入,它就会工作。 - Ravindra_Bhati
1
在设备上,第一次使用也很好,但是对于接下来的点击,您需要非常有礼貌地(几乎是长时间的轻触)在按钮本身下方某个位置进行操作,这很奇怪且不符合预期行为。 - avdyushin
2个回答

23

问题在于当页面关闭时,导航栏按钮布局不佳(似乎约束条件错误)。

在视图层次结构调试中清晰可见:

演示

这里是一个修复方案(当然只是一种变通方法,但很安全,因为即使问题得到解决,它也将继续工作)。思路并不是与破损的布局斗争,而是创建另一个按钮,这样布局引擎本身会删除旧的坏按钮并添加新的按钮来刷新布局。用于此的工具相当常见 - 使用 .id()

演示2

因此,修改后的代码如下:

struct ContentView: View {

    @State var showSheetView = false
    @State private var navigationButtonID = UUID()
    
    var body: some View {
        NavigationView {
            Group {
                Text("Master")
                Button(action: { self.showSheetView.toggle() }) {
                    Text("Button 1")
                }
            }
            .navigationBarTitle("Main")
            .navigationBarItems(trailing: Button(action: {
                self.showSheetView.toggle()
            }) {
                Text("Button 2").bold() // recommend .padding(.vertical) here
            }
            .id(self.navigationButtonID)) // force new instance creation
        }
        .sheet(isPresented: $showSheetView) {
            DetailView(isPresented: self.$showSheetView)
                .onDisappear {
                    // update button id after sheet got closed
                    self.navigationButtonID = UUID()
                }
        }
    }
}

1
苹果公司在xCode版本11.5(11E608c)中仍未解决这个问题。 - Mark
4
在Xcode 12.5 (12E262)中仍然存在这个问题。 - A. Poltoratskyi
2
Xcode 13.0 beta(13A5154h)- 仍然看到这个问题。 - LinusGeffarth
这对我似乎不起作用,唯一能让它工作的方法是将其从表单呈现更改为全屏覆盖。 - Mohammed Shakeer
尽管已经到了2023年,但这个错误仍然存在,@Asperi你是最棒的。 - Metin Atalay
1
在Xcode 14.2中仍然遇到这个问题! - Jason Moore

0
感谢Asperi的回答,但仍然存在一个错误。
他说要使用id,但是当NavigationView中有scroll,并且我们刷新这个id时,scroll会回到顶部。
您可以使用以下解决方案。
content
        .onDisappear {
            let scene = UIApplication.shared.connectedScenes.first as? UIWindowScene
            if let viewFrame = scene?.windows.first?.rootViewController?.view.frame {
                scene?.windows.first?.rootViewController?.view.frame = .zero
                scene?.windows.first?.rootViewController?.view.frame = viewFrame
            }
        }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接