SwiftData:和 SwiftUI 一起实现删除修改

513 阅读4分钟

我们在讲解 SwiftData 的第一篇文章中,介绍了如何将模型与 SwiftData 结合在一起,第二篇文章讲解了如何在 SwiftUI 中添加数据和查询并展示数据。

下一步就是有趣的部分就是添加一些用户界面,让用户创建、编辑和删除 SwiftData 对象,而不是依赖于示例数据。

删除

其中最简单的是删除,所以我们从这里开始。你可以通过将任何对象传递给模型上下文的 delete() 方法来从 SwiftData中 删除它。

在我们的代码中,我们使用了 ForEach 来遍历 SwiftData 查询返回的所有 person 实例,因此我们现在可以编写与使用 SwiftUI 处理任何数据数组时相同的删除方法。 将此方法添加到 ContentView 中:

func deletePerson(indexSet: IndexSet) {
    for index in indexSet {
        let person = persons[index]
        modelContext.delete(person)
        
    }
}

然后为了模拟删除事件,我们可以给 ForEach 尾部添加以下修饰符:

.onDelete(perform: deletePerson)

点击删除按钮即可删除当前数据,效果图如下:

截屏2024-11-22 21.53.33.png

修改

接下来是编辑数据,这意味着需要使用各种选项创建一个新的 SwiftUI 视图:

  • 文本字段,用于编辑目的地的名称和详细信息属性。
  • 一个用于调整优先级的选取器。

如果我们把所有这些内容都放入一个表单视图中,那么默认情况下,布局将会非常棒。 所以,现在按 Cmd+N 创建一个新的 SwiftUI 视图,并命名为 EditPersonView。当 Xcode 打开它进行编辑时,请在顶部附近添加 import SwiftData,以便我们可以访问所有的 SwiftData API。

这需要知道选择的 person 实例。如果我们只是想从person 实例读取属性,我们可以像这样添加一个 EditPersonView 属性:

var person: Person

但在这里,仅仅从目标实例对象读取属性是不够的,我们需要能够将它们绑定到 SwiftUI 视图进行数据同步,如 TextFieldPicker,这样用户就可以实际编辑这些值。

所以,我们需要使用一个名为 @Bindable 的属性包装器,它能够创建任何 SwiftData 对象的绑定。这是为 iOS 17 中引入的 Swift 观察而构建的,但由于 SwiftData 建立在观察的基础上,它在这里也同样有效。上面的代码修改如下:

@Bindable var person: Person

我们添加完这个属性后,会发现我们的预览试图会报错。更糟糕的是,我们不能在预览中创建一个临时 person 实例,因为 SwiftData 不知道在哪里创建它,SwiftData找不到有效的模型容器或上下文。

为了解决这个问题,我们需要手动创建一个模型容器,我们将以一种非常特殊的方式做到这一点:因为这是一个包含示例数据的预览代码,我们将创建一个内存容器,这样我们创建的任何预览对象都不会被保存,而是暂时的。 这需要四个步骤:

  • 创建自定义 ModelConfiguration 对象以指定我们想要的内存存储。
  • 使用它来创建模型容器。
  • 创建包含一些示例数据的示例 person 对象。这将在我们刚刚创建的模型容器内自动创建。
  • 将该示例对象和我们的模型容器发送到 EditPersonView,然后全部返回。

在之前的实现中,我们不需要执行步骤1和2,因为这一切都由 SwiftDataDemoApp.swift 中的 modelContainer 修饰符处理,但现在我们需要手动完成,这样我们就可以创建一个 person 对象传递到视图中。 将预览代码修改为:

#Preview {
    do {
        let config = ModelConfiguration(isStoredInMemoryOnly: true)
        let container = try ModelContainer(for: Person.self, configurations: config)
        
        let example = Person(name: "Mike", address: "Street-3")
        return EditPersonView(person: example)
            .modelContainer(container)
    } catch {
        fatalError("Failed to create model container.")
    }
}

Tips:如果你尝试创建 SwiftData 模型的实例,但没有有效的模型容器,预览会崩溃。

接下来就是修改 EditPersonView 中的 UI 代码:

@Bindable var person: Person

var body: some View {
    Form {
        TextField("Name", text: $person.name)
        TextField("Address", text: $person.address, axis: .vertical)
        Section("Level") {
            Picker("Level", selection: $person.level) {
                Text("Max").tag(1)
                Text("Middle").tag(2)
                Text("Small").tag(3)
            }
            .pickerStyle(.segmented)
        }

    }
    .navigationTitle("Edit Person")
    .navigationBarTitleDisplayMode(.inline)
}

效果图如下:

截屏2024-11-22 21.59.04.png 编辑页面写好,接下来就是编写从首页跳转的逻辑:

var body: some View {
    NavigationStack {
        List {
            ForEach(persons) { person in
                NavigationLink(value: person) {
                    VStack(alignment: .leading) {
                        Text(person.name)
                            .font(.headline)
                        
                        Text(person.address)
                    }
                }
            }
            .onDelete(perform: deletePerson)
        }
        .navigationDestination(for: Person.self, destination: EditPersonView.init)
        .navigationTitle("SwiftDataDemo")
        .toolbar { Button("添加数据", action: addData) }
    }
}

上述代码的修改是在ForEach的中添加了 NavigationLink,然后在 navigationTitle 下面添加 navigationDestination 修饰符,这样 SwiftUI 就知道在选择目标时跳转到我们的编辑视图了。