在SwiftUI中,iPad弹出视图中的NavigationView无法正常工作。

14

我有以下代码,当按钮被点击时显示一个弹出框:

struct ContentView: View {

    @State private var show = false

    var body: some View {

        Button("Open") {
            self.show.toggle()
        }.popover(isPresented: $show, content: {
//            NavigationView {
                ScrollView {
                    ForEach(0...10, id: \.self) {_ in
                        Text("Test popover ...")
                    }.padding()
                }
//            }
        })

    }
}

enter image description here

如果我在弹出窗口的内容中添加一个NavigationView,那么我会得到这个:

enter image description here

你有什么想法为什么会发生这种情况?

如果我为内容设置一个固定的框架,它就可以正常工作。但是我不想那样做,因为我希望气泡窗口根据其内容进行调整大小。


你找到了解决方法,还是已经向苹果报告了这个错误? - Olivier
2个回答

8

Asperi的回答非常好而全面,但我想为那些懒惰的人添加一个方法。

这个小弹出窗口是在iPadOS 13.4中引入的一个错误(弹出框在13.0.x - 13.3.x版本中显示正常)。我已经提交了FB7640734的错误报告,目前显示“少于10个”相似的报告,并且仍然处于开放状态。

易于操作的解决方法,我在基于SwiftUI编写的生产应用程序中使用,它运行在iOS、iPadOS和Mac Catalyst上,只需在NavigationView后添加以下内容:

.frame(minWidth: 320, idealWidth: 400, maxWidth: nil, minHeight: 500, idealHeight: 700, maxHeight: nil, alignment: .top)

即在原帖示例代码的上下文中:
struct ContentView: View {

    @State private var show = false

    var body: some View {

        Button("Open") {
            self.show.toggle()
        }.popover(isPresented: $show, content: {
            NavigationView {
                ScrollView {
                    ForEach(0...10, id: \.self) {_ in
                        Text("Test popover ...")
                    }.padding()
                }
            }.frame(minWidth: 320, idealWidth: 400, maxWidth: nil,
                    minHeight: 500, idealHeight: 700, maxHeight: nil,
                    alignment: .top)
        })

    }
}

此设置了一个适当大小的弹出窗口,它将在320-400像素之间扩展宽度,高度为500x700像素,在实践中,这是一个很好的弹出窗口大小(如果更大,您可能应该使用其他东西而不是弹出窗口)。


iOS15仍然存在小弹出窗口的问题。 - Peter Lapisu

8

可能在iPad上,他们在尺寸检测方面遇到了先有鸡还是先有蛋的问题,所以只能用最小值来完成。

无论如何,解决方案就是明确设置.frame,可以使用预定义的值(对于iPad来说并不太糟糕),或者通过GeometryReader从外部框架动态计算。

这里是一个例子。已在Xcode 12 / iPadOS 14上进行了测试

demo

struct TestPopover: View {

    @State private var show = false

    var body: some View {
        GeometryReader { gp in
            VStack {
                Button("Open") {
                    self.show.toggle()
                }.popover(isPresented: $show, content: {
                    NavigationView {
                        ScrollView {   // or List
                            ForEach(0...10, id: \.self) {_ in
                                Text("Test popover ...")
                            }.padding()
                        }
                        .navigationBarTitle("Test", displayMode: .inline)
                    }
                    .frame(width: gp.size.width / 3, height: gp.size.height / 3)
                })
            }.frame(maxWidth: .infinity, maxHeight: .infinity)
        }
    }
}

变体2: 部分计算外部尺寸,部分计算内部尺寸。

demo2

struct TestPopover: View {

    @State private var show = false
    @State private var popoverWidth = CGFloat(100)

    var body: some View {
        GeometryReader { gp in
            VStack {
                Button("Open") {
                    self.show.toggle()
                }.popover(isPresented: $show, content: {
                    NavigationView {
                        ScrollView {   // or List
                            ForEach(0...10, id: \.self) {_ in
                                Text("Test popover ...").fixedSize()
                            }.padding()
                            .background(GeometryReader {
                                Color.clear
                                    .preference(key: ViewWidthKey.self, value: $0.frame(in: .local).size.width)
                            })
                            .onPreferenceChange(ViewWidthKey.self) {
                                self.popoverWidth = $0
                            }
                        }
                        .navigationBarTitle("Test", displayMode: .inline)
                    }
                    .frame(width: self.popoverWidth, height: gp.size.height / 3)
                })
            }.frame(maxWidth: .infinity, maxHeight: .infinity)
        }
    }
}

struct ViewWidthKey: PreferenceKey {
    typealias Value = CGFloat
    static var defaultValue = CGFloat.zero
    static func reduce(value: inout Value, nextValue: () -> Value) {
        value += nextValue()
    }
}


这解决了宽度的问题,但实际上我可以为此设置固定大小,更重要的是弹出窗口的高度。假设我在弹出窗口中有一个List,我需要用户能够尽可能地看到列表中的内容而无需滚动,因此弹出窗口应该适应列表的内容大小,你有任何想法如何获取这个值吗? - Sorin Lica
ScrollView/List的高度可以是无限的(如果有数百行),因此可能超出屏幕。您可以像宽度一样计算高度,并决定是否限制它(如果超出屏幕高度)或分配为已计算的高度。例如,请参见SwiftUI:仅在超出屏幕高度时使ScrollView可滚动 - Asperi

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