表单中的 SwiftUI 选择器 - 无法动态调整表单大小 Space

SwiftUI Picker in Form - Can't Dynamically Size the Form Space

我正在为一个视图而苦苦挣扎,我想在其中嵌入多个选择器 其他意见。当我将选择器包装在一个表单中时,我得到了所需的行为 选择器,但在选择器周围有很多额外的 space 我似乎看不到 自动调整。

这是一个例子-红色轮廓中的space似乎是由另一个决定的 查看元素不是选择器的大小。

我当然可以硬编码窗体的框架高度,但这是反复试验 并且只会特定于设备和方向。我试过多次 Stacks inside Stacks with padding, GeometryReader 等版本,但我还没有想出任何 解决方案。顺便说一句,我确实想要选择器标签,否则我可以删除 表格。

我也尝试在 init() 中设置 UITableView.appearance().tableFooterView,但这也不起作用。

这是一个简化版本:

struct ContentView4: View {
    @State var selectedNumber1: Int = 1
    @State var selectedNumber2: Int = 2
    @State var selectedNumber3: Int = 3

    var body: some View {
        NavigationView {
            VStack(alignment: .leading) {
                HStack {
                    Spacer()
                    Text("Compare up to 3")
                        .font(.caption)
                    Spacer()
                }//h
            
                Form {//for pickers
                        Picker(selection: $selectedNumber1, label: Text("A")) {
                            ForEach(0..<10) {
                                Text("\([=11=])")
                            }
                        }//picker

                        Picker(selection: $selectedNumber2, label: Text("B")) {
                            ForEach(0..<10) {
                                Text("\([=11=])")
                            }
                        }//picker

                        Picker(selection: $selectedNumber3, label: Text("C")) {
                            ForEach(0..<10) {
                                Text("\([=11=])")
                            }
                        }//picker
                }//form for pickers
                .padding(.horizontal, 10)
                //.frame(height: 200) //don't want to hard code this
            
                VStack(alignment: .leading) {
                    HStack {
                        Text("A")
                            .frame(width: 100)
                        Text("B")
                            .frame(width: 100)
                        Text("C")
                            .frame(width: 100)
                    }
                    .padding(.horizontal, 10)
                
                    ScrollView(.vertical, showsIndicators: false) {
                        VStack(alignment: .leading){
                            Text("A title line")
                                .font(.headline)
                                .padding(.vertical, 5)
                            HStack {
                                Text("Number")
                                    .frame(width: 100)
                                Text("Number")
                                    .frame(width: 100)
                                Text("Number")
                                    .frame(width: 100)
                            }
                            Text("Another title line")
                                .font(.headline)
                                .padding(.vertical, 5)
                            HStack {
                                Text("Something")
                                    .frame(width: 100)
                                Text("Something")
                                    .frame(width: 100)
                                Text("Something")
                                    .frame(width: 100)
                            }
                            Text("A Third title line")
                                .font(.headline)
                                .padding(.vertical, 5)
                            HStack {
                                Text("More")
                                    .frame(width: 100)
                                Text("More")
                                    .frame(width: 100)
                                Text("More")
                                    .frame(width: 100)
                            }
                        }
                    }//scroll
                    .padding(.horizontal, 10)
                }
                .navigationBarTitle("Compare Three", displayMode: .inline)
            }
        }//nav
    }//body
}//struct

有趣的是,我能够通过删除表单并包装每个表单来获得解决方案 菜单中的选择器,如下所示:

Menu {
    Picker(selection: $selectedNumber2, label: EmptyView()) {
        ForEach(0..<10) {
            Text("\([=12=])")
        }
    }//picker
} label: {
    HStack {
    Text("B")
        Spacer()
        Image(systemName: "chevron.right")
            .resizable()
            .frame(width: 14, height: 14)
    }//h
}//menu label

不过,如果能自动配置的话,我还是比较喜欢这个Form的样子 space 围绕表单项。

如有任何指导,我们将不胜感激。 Xcode13.4,iOS15.5

Form(和 List)并不意味着像这样堆叠在其他视图中,这就是为什么它有如此奇怪的行为。

幸运的是,使用 NavigationLink 重新创建您想要的内容相当简单。这是几个自定义视图的简单示例:

// drop-in NavigationLink replacement for Picker
struct NavigationButton<Content: View, SelectionValue: Hashable> : View {
    @Binding var selection: SelectionValue
    @ViewBuilder let content: () -> Content
    @ViewBuilder let label: () -> Text
    
    var body: some View {
        NavigationLink {
            PickerView(selection: $selection, content: content, label: label)
        } label: {
            HStack {
                label()
                Spacer()
                Text(String(describing: selection))
                    .foregroundColor(.secondary)
            }
            .contentShape(Rectangle())
        }
        .buttonStyle(NavigationLinkButtonStyle())
    }
}

// subview for the Picker page, which lets us use `dismiss()` 
// to pop the subview when the user selects an option
struct PickerView<Content: View, SelectionValue: Hashable> : View {
    @Binding var selection: SelectionValue
    @ViewBuilder let content: () -> Content
    @ViewBuilder let label: () -> Text
    @Environment(\.dismiss) private var dismiss
    
    var body: some View {
        Form {
            Picker(selection: $selection, content: content, label: label)
                .pickerStyle(.inline)
                .labelsHidden()
                .onChange(of: selection) { _ in
                    dismiss()
                }
        }
        .navigationTitle(label())
    }
}

// recreate the appearance of a List row
struct NavigationLinkButtonStyle: ButtonStyle {
    func makeBody(configuration: Configuration) -> some View {
        HStack {
            configuration.label
                .frame(maxWidth: .infinity)
            Image(systemName: "chevron.right")
                .font(.footnote.bold())
                .foregroundColor(Color(UIColor.tertiaryLabel))
        }
        .padding()
        .background(
            Rectangle()
                .fill(configuration.isPressed ? Color(UIColor.quaternaryLabel) : Color(UIColor.systemBackground))
        )
    }
}

如果您喜欢使用 Form 得到的 .insetGrouped 样式,我们可以通过将 NavigationButton 放入剪裁的 VStack:

来复制它
VStack(spacing: 0) {

    NavigationButton(selection: $selectedNumber1) {
        ForEach(0..<10) {
            Text("\([=11=])")
        }
    } label: {
        Text("A")
    }
    
    Divider()
    
    NavigationButton(selection: $selectedNumber2) {
        ForEach(0..<10) {
            Text("\([=11=])")
        }
    } label: {
        Text("B")
    }
}
.clipShape(RoundedRectangle(cornerRadius: 11))
.padding()
.background(Color(UIColor.systemGroupedBackground))

这是一张屏幕截图,显示了我在您的原始视图上方的自定义视图 Form

(如果您喜欢 Picker 作为弹出菜单,您可以使用 Menu 而不是 NavigationLink)