MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

SwiftUI Form与List组件

2024-03-085.1k 阅读

SwiftUI 中的 Form 组件

Form 组件基础介绍

在 SwiftUI 应用开发中,Form 组件是一种用于创建表单的容器视图。它为用户输入和显示信息提供了一种结构化且易于交互的方式,非常适合创建设置界面、用户注册表单等需要用户输入或选择的场景。

Form 组件会自动对其包含的子视图进行分组和样式处理,以符合平台的设计规范。例如,在 iOS 上,Form 中的元素会呈现出与系统设置应用相似的外观,具有清晰的分隔线和合适的间距,为用户提供熟悉且友好的交互体验。

创建简单的 Form

下面通过一个简单的代码示例来展示如何创建一个基本的 Form

import SwiftUI

struct ContentView: View {
    var body: some View {
        Form {
            Section(header: Text("基本信息")) {
                Text("姓名")
                TextField("请输入姓名", text: .constant(""))
                Text("年龄")
                TextField("请输入年龄", text: .constant(""))
            }
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

在上述代码中,首先创建了一个 Form 容器。然后在 Form 中添加了一个 SectionSection 可以对表单内容进行分组,这里为其设置了一个标题 “基本信息”。在 Section 内部,添加了一些文本标签和 TextField 用于用户输入。

Section 的深入使用

SectionForm 中用于组织内容的重要元素。除了设置简单的标题,还可以对其进行更多定制。

  1. 无标题 Section:有时候我们可能不需要显示标题,可以创建一个无标题的 Section,代码如下:
Form {
    Section {
        Text("无标题部分内容")
    }
}
  1. 设置 FooterSection 不仅可以有 header,还能设置 footer,用于显示一些额外的说明信息。例如:
Form {
    Section(header: Text("登录信息"), footer: Text("请确保信息准确无误")) {
        TextField("用户名", text: .constant(""))
        SecureField("密码", text: .constant(""))
    }
}
  1. 嵌套 SectionSection 可以进行嵌套,以实现更复杂的内容组织。比如:
Form {
    Section(header: Text("联系方式")) {
        Section(header: Text("手机")) {
            TextField("手机号码", text: .constant(""))
        }
        Section(header: Text("邮箱")) {
            TextField("邮箱地址", text: .constant(""))
        }
    }
}

Form 中的交互元素

  1. ToggleToggle 是一个开关按钮,常用于设置一些布尔值类型的选项。在 Form 中使用 Toggle 示例如下:
struct ToggleInFormView: View {
    @State var isNotificationOn = true
    var body: some View {
        Form {
            Section(header: Text("通知设置")) {
                Toggle("接收通知", isOn: $isNotificationOn)
            }
        }
    }
}
  1. PickerPicker 用于从一组选项中选择一个值。有不同的样式可供选择,例如 WheelPickerStyleSegmentedPickerStyle 等。下面是一个在 Form 中使用 Picker 的示例:
struct PickerInFormView: View {
    @State var selectedColor = "红色"
    let colors = ["红色", "绿色", "蓝色"]
    var body: some View {
        Form {
            Section(header: Text("颜色选择")) {
                Picker("选择颜色", selection: $selectedColor) {
                    ForEach(colors, id: \.self) { color in
                        Text(color)
                    }
                }
                .pickerStyle(SegmentedPickerStyle())
            }
        }
    }
}
  1. StepperStepper 用于增加或减少一个数值。在 Form 中使用 Stepper 示例如下:
struct StepperInFormView: View {
    @State var quantity = 1
    var body: some View {
        Form {
            Section(header: Text("商品数量")) {
                Stepper("数量: \(quantity)", value: $quantity, in: 1...10)
            }
        }
    }
}

SwiftUI 中的 List 组件

List 组件基础介绍

List 组件在 SwiftUI 中用于展示一系列数据。它是一种可滚动的视图,能够高效地显示大量的数据项,并且为每个数据项提供了合适的布局和交互方式。与 Form 不同,List 更侧重于数据展示,虽然也可以包含交互元素,但通常不像 Form 那样专注于表单输入。

List 会根据数据的数量和设备屏幕的大小自动进行布局调整,以确保用户能够方便地浏览数据。在 iOS 上,List 的外观通常类似于系统的联系人列表或邮件列表,具有简洁、清晰的视觉效果。

创建简单的 List

下面通过一个简单的代码示例来展示如何创建一个基本的 List

import SwiftUI

struct ContentView: View {
    let fruits = ["苹果", "香蕉", "橙子", "葡萄"]
    var body: some View {
        List(fruits, id: \.self) { fruit in
            Text(fruit)
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

在上述代码中,定义了一个包含水果名称的数组 fruits。然后使用 List 来展示这个数组中的数据,List 的初始化方法接收数据数组和一个标识每个数据项唯一性的 id 参数(这里使用数据项自身作为 id)。在闭包中,为每个数据项创建了一个 Text 视图来显示水果名称。

List 的数据绑定与动态更新

  1. 使用 @State 进行数据绑定:当需要动态更新 List 中的数据时,可以使用 @State 来绑定数据。例如,添加一个按钮来动态添加新的水果到列表中:
struct DynamicListView: View {
    @State var fruits = ["苹果", "香蕉"]
    @State var newFruit = ""
    var body: some View {
        VStack {
            List(fruits, id: \.self) { fruit in
                Text(fruit)
            }
            HStack {
                TextField("新水果", text: $newFruit)
                Button("添加") {
                    fruits.append(newFruit)
                    newFruit = ""
                }
            }
        }
    }
}

在这个示例中,@State 修饰的 fruits 数组用于存储水果列表数据,@State 修饰的 newFruit 用于接收用户输入的新水果名称。当用户点击 “添加” 按钮时,新水果被添加到 fruits 数组中,List 会自动更新显示。

  1. 使用 @ObservedObject 进行数据绑定:对于更复杂的数据模型和数据管理,可以使用 @ObservedObject。首先创建一个数据模型和视图模型:
class FruitViewModel: ObservableObject {
    @Published var fruits = ["苹果", "香蕉"]
    func addFruit(_ fruit: String) {
        fruits.append(fruit)
    }
}

struct ObservedObjectListView: View {
    @ObservedObject var viewModel = FruitViewModel()
    @State var newFruit = ""
    var body: some View {
        VStack {
            List(viewModel.fruits, id: \.self) { fruit in
                Text(fruit)
            }
            HStack {
                TextField("新水果", text: $newFruit)
                Button("添加") {
                    viewModel.addFruit(newFruit)
                    newFruit = ""
                }
            }
        }
    }
}

在上述代码中,FruitViewModel 是一个视图模型类,使用 @ObservableObject 修饰,其中的 fruits 数组使用 @Published 修饰,以通知视图数据发生了变化。ObservedObjectListView 视图通过 @ObservedObject 绑定到 viewModel,当调用 viewModeladdFruit 方法时,List 会自动更新。

List 的样式与定制

  1. 分组 List:可以将 List 中的数据项进行分组,通过在 List 闭包中使用 Section 来实现。例如:
struct GroupedListView: View {
    let fruits = ["苹果", "香蕉", "橙子"]
    let vegetables = ["胡萝卜", "西兰花", "菠菜"]
    var body: some View {
        List {
            Section(header: Text("水果")) {
                ForEach(fruits, id: \.self) { fruit in
                    Text(fruit)
                }
            }
            Section(header: Text("蔬菜")) {
                ForEach(vegetables, id: \.self) { vegetable in
                    Text(vegetable)
                }
            }
        }
    }
}
  1. 设置 List 样式:SwiftUI 提供了几种不同的 ListStyle,可以通过 listStyle 修饰符来设置。例如,PlainListStyle 可以使 List 具有更简洁的外观,没有默认的分隔线和背景样式:
struct PlainListStyleView: View {
    let fruits = ["苹果", "香蕉", "橙子"]
    var body: some View {
        List(fruits, id: \.self) { fruit in
            Text(fruit)
        }
       .listStyle(PlainListStyle())
    }
}
  1. 自定义数据项样式:除了整体的 List 样式,还可以自定义每个数据项的样式。例如,为水果列表中的每个数据项添加一个背景颜色:
struct CustomCellListView: View {
    let fruits = ["苹果", "香蕉", "橙子"]
    var body: some View {
        List(fruits, id: \.self) { fruit in
            Text(fruit)
               .padding()
               .background(Color.yellow)
               .cornerRadius(10)
        }
    }
}

List 的交互功能

  1. 点击交互:可以为 List 中的数据项添加点击交互。例如,当点击某个水果时,导航到一个详细信息页面。首先创建一个详细信息视图:
struct FruitDetailView: View {
    let fruit: String
    var body: some View {
        VStack {
            Text("这是 \(fruit) 的详细信息")
        }
    }
}

然后在 List 视图中添加点击交互:

struct ListTapInteractionView: View {
    let fruits = ["苹果", "香蕉", "橙子"]
    @State var selectedFruit: String? = nil
    var body: some View {
        NavigationView {
            List(fruits, id: \.self) { fruit in
                Button(action: {
                    selectedFruit = fruit
                }) {
                    Text(fruit)
                }
            }
           .navigationTitle("水果列表")
           .sheet(item: $selectedFruit) { fruit in
                FruitDetailView(fruit: fruit)
            }
        }
    }
}

在上述代码中,通过 Button 为每个水果数据项添加了点击动作,点击后将 selectedFruit 设置为对应的水果名称。然后使用 sheet 来显示 FruitDetailView,展示水果的详细信息。

  1. 删除交互:为 List 中的数据项添加删除交互也是常见的需求。可以通过 onDelete 修饰符来实现。例如:
struct ListDeleteInteractionView: View {
    @State var fruits = ["苹果", "香蕉", "橙子"]
    var body: some View {
        List {
            ForEach(fruits, id: \.self) { fruit in
                Text(fruit)
            }
           .onDelete { indexSet in
                fruits.remove(atOffsets: indexSet)
            }
        }
    }
}

在这个示例中,onDelete 闭包接收一个 indexSet,表示要删除的数据项的索引集合。通过 fruits.remove(atOffsets: indexSet) 方法从数组中删除对应的水果数据,List 会自动更新显示。

Form 与 List 的对比与选择

功能侧重对比

  1. Form:主要侧重于创建用户输入表单。它通过 Section 对输入元素进行分组,提供了一种结构化的方式来呈现表单内容。Form 中的元素如 TextFieldTogglePicker 等都紧密围绕用户输入和设置选项的功能设计。例如,在一个应用的设置界面中,使用 Form 可以方便地创建各种设置开关、选择列表和文本输入框,让用户能够轻松地调整应用的各种参数。
  2. List:重点在于数据展示和浏览。它适合展示大量的数据项,并且可以通过分组、样式定制等方式优化数据的呈现。虽然 List 也可以包含交互元素,但交互功能更多是围绕数据项的查看、选择或操作,而不是像 Form 那样以用户输入为核心。比如,展示新闻列表、联系人列表等场景,List 是更合适的选择。

外观样式对比

  1. Form:在 iOS 平台上,Form 通常具有类似系统设置应用的外观。它会自动为 Section 添加分隔线和合适的间距,TextField 等输入元素也会呈现出与系统风格一致的样式。这种外观设计使得 Form 在创建设置表单等场景时,能够与系统的整体风格保持高度一致,为用户提供熟悉且专业的视觉体验。
  2. ListList 的外观相对更加灵活多样。默认情况下,它具有类似于系统列表的样式,数据项之间有分隔线。但通过 listStyle 等修饰符,可以轻松地改变其外观,如使用 PlainListStyle 获得更简洁的无分隔线外观,或者通过自定义数据项样式来满足各种不同的设计需求。在展示数据时,List 能够根据具体需求进行更个性化的样式定制。

适用场景选择

  1. Form 的适用场景
    • 设置界面:如应用的通用设置、账户设置等,用户需要在这里输入信息、切换开关或选择选项来配置应用的功能。
    • 用户注册/登录表单:包含用户名、密码输入框,以及记住密码、注册协议勾选等元素,使用 Form 可以将这些元素组织得清晰明了。
    • 数据录入表单:例如在一个数据采集应用中,需要用户填写各种信息,Form 能够提供一个结构化的输入界面。
  2. List 的适用场景
    • 内容列表展示:像新闻应用中的新闻列表、音乐应用中的歌曲列表等,用户主要是浏览和选择内容。
    • 导航菜单:可以将 List 作为导航菜单的一种呈现方式,通过点击不同的数据项跳转到不同的页面。
    • 可操作的数据列表:例如一个待办事项列表,用户可以对每个事项进行勾选完成、删除等操作,List 能够很好地承载这些交互功能。

在实际的 SwiftUI 应用开发中,准确理解 FormList 的特点,并根据具体的需求选择合适的组件,能够有效地提升应用的用户体验和开发效率。同时,还可以结合使用这两个组件,例如在 Form 中嵌入 List 来展示一些可选择的数据列表,或者在 List 中添加表单元素来实现更复杂的交互场景。通过灵活运用它们的特性,可以创建出功能丰富、界面美观的应用程序。