【问题标题】:NavigationView in iPad popover does not work properly in SwiftUIiPad 弹出窗口中的 NavigationView 在 SwiftUI 中无法正常工作
【发布时间】:2020-04-15 11:52:02
【问题描述】:

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

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()
                }
//            }
        })

    }
}

如果我在弹出框的内容中添加NavigationView,那么我会得到:

知道为什么会这样吗?

如果我为内容设置一个固定的框架,它工作正常,但我不想这样做,因为我希望弹出框根据它的内容调整大小。

【问题讨论】:

  • 您是否找到了解决方法,或者是否有与 Apple 相关的错误报告?

标签: ios ipad swiftui


【解决方案1】:

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

无论如何,解决方案是显式设置.frame,或者使用预定义的值(对于iPad来说还不错),或者使用动态计算的(例如,通过GeometryReader从外框)

这是一个例子。使用 Xcode 12 / iPadOS 14 测试

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:部分根据外部尺寸计算,部分根据内部尺寸计算。

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,我需要用户为了能够在不滚动的情况下从列表中尽可能多地看到,所以弹出框应该适应列表的内容大小,你知道如何获得这个值吗?
  • ScrollView/List 的高度可以是无限的(如果有数百行的话),所以超出屏幕。您可以计算与显示的宽度相同的高度,并决定是否限制它(如果它超出屏幕高度)或按计算分配。例如,请参阅SwiftUI: Make ScrollView scrollable only if it exceeds the height of the screen
【解决方案2】:

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)

即在 OP 示例代码的上下文中:

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 点之间展开,这实际上是弹出框的合适尺寸(任何更大,您可能应该使用弹出框以外的其他东西)。

【讨论】:

    猜你喜欢
    • 2013-07-07
    • 2015-01-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-02-15
    • 1970-01-01
    • 2013-10-17
    • 1970-01-01
    相关资源
    最近更新 更多