SwiftUI如何使用Stack布局实现类似Bootstrap的双列非滚动表单
SwiftUI 双列表单实现问题
我正在尝试创建效果如下的表单:
该表单使用Bootstrap制作十分简单,但我目前学习SwiftUI时最多只能实现如下效果:
我尝试使用VStack、ZStack、HStack布局,但效果不符合预期,可能是使用方式有误,需要实现和Bootstrap版本相似的表单。
当前使用的SwiftUI代码如下:
struct FontsGroup: View { @State var username: String = "" @State var isPrivate: Bool = true var body: some View { ZStack{ VStack{ HStack( alignment: .top, spacing: 0 ) { Form{ Section(header: HStack { Text("Nurašoma Prekė") Image(systemName: "staroflife.fill").foregroundColor(.red) } ) { TextField("Username", text: $username) Toggle(isOn: $isPrivate) { Text("Private Account") } } }.listRowInsets(EdgeInsets()) Form{ Section(header: HStack { Text("Nurašymo priežastis") Image(systemName: "staroflife.fill").foregroundColor(.red) } ) { TextField("Username", text: $username) Toggle(isOn: $isPrivate) { Text("Private Account") } } }.listRowInsets(EdgeInsets()) } HStack( alignment: .top, spacing: 0 ) { Form{ Section(header: HStack { Text("Nurašoma Prekė") Image(systemName: "staroflife.fill").foregroundColor(.red) } ) { TextField("Username", text: $username) Toggle(isOn: $isPrivate) { Text("Private Account") } } } Form{ Section(header: HStack { Text("Nurašymo priežastis") Image(systemName: "staroflife.fill").foregroundColor(.red) } ) { TextField("Username", text: $username) Toggle(isOn: $isPrivate) { Text("Private Account") } } } } }.padding() } } }
我的目标是得到一个可配置任意行数的双列表单视图,另外我发现使用上述Stack布局时每个表单元素都可滚动,请问如何避免该问题?
解决方案
1. 解决单表单项滚动问题
SwiftUI的Form本质基于List实现,默认自带滚动特性。你嵌套了多个独立Form,才会出现每个表单项都可以独立滚动的问题,两种修复方案:
- iOS 16+直接给每个Form添加
.scrollDisabled(true)修饰符即可禁用滚动 - 兼容更低版本可以直接弃用Form,用
VStack+Section配合系统分组背景样式模拟表单效果,本身不具备滚动特性
2. 实现可配置双列表单
可以抽离表单项为通用组件,用LazyVGrid实现自动双列排布,仅需修改配置数组即可任意增减表单项数量,参考实现代码如下:
// 表单项类型枚举,可按需扩展选择器、日期选择等类型 enum FormFieldType { case text(Binding<String>) case toggle(Binding<Bool>) } // 表单项数据模型 struct FormField: Identifiable { let id = UUID() let title: String let required: Bool let type: FormFieldType } // 复用表单组件 struct FormItemView: View { let field: FormField var body: some View { Section { switch field.type { case .text(let bindStr): TextField("请输入", text: bindStr) case .toggle(let bindBool): Toggle(isOn: bindBool) { Text("Private Account") } } } header: { HStack(spacing: 4) { Text(field.title) if field.required { Image(systemName: "staroflife.fill") .foregroundColor(.red) .font(.caption) } } } .listRowInsets(.init(top: 8, leading: 12, bottom: 8, trailing: 12)) } } // 最终双列表单视图 struct TwoColumnForm: View { // 按需配置表单项即可,自动适配双列换行 private let fields: [FormField] @State private var field1 = "" @State private var toggle1 = true @State private var field2 = "" @State private var toggle2 = true @State private var field3 = "" @State private var toggle3 = true @State private var field4 = "" @State private var toggle4 = true init() { fields = [ FormField(title: "Nurašoma Prekė", required: true, type: .text($field1)), FormField(title: "Nurašymo priežastis", required: true, type: .toggle($toggle1)), FormField(title: "Nurašoma Prekė", required: true, type: .text($field2)), FormField(title: "Nurašymo priežastis", required: true, type: .toggle($toggle2)), FormField(title: "Nurašoma Prekė", required: true, type: .text($field3)), FormField(title: "Nurašymo priežastis", required: true, type: .toggle($toggle3)), // 新增表单项会自动排布到下一行 ] } var body: some View { ScrollView { // 整个表单需要滚动仅需在外层加一个ScrollView即可 LazyVGrid(columns: [.init(.flexible(), spacing: 16), .init(.flexible(), spacing: 16)], spacing: 16) { ForEach(fields) { field in VStack(spacing: 0) { List { FormItemView(field: field) } .listStyle(.grouped) .scrollDisabled(true) .frame(height: 120) // 可根据表单项高度自适应调整 } } } .padding() } .background(Color(UIColor.systemGroupedBackground)) } }
如果需要兼容iOS 15及更低版本,把LazyVGrid替换为每两个元素包一个HStack的分组逻辑即可实现同样效果。
内容的提问来源于stack exchange,提问作者ToothFairy
相关产品推荐
相关产品推荐

