在SwiftUI中可以通过ForEach的onDelete方法删除列表项,但因为onDelete方法不提供多选删除的直接支持,因此可以设置选择状态并在编辑模式下启用批量删除功能。
实现批量删除的思路
1、管理选中状态:引入一个字典来存储每个书籍项的选中状态。
2、批量删除按钮:在 Edit 模式下显示一个“批量删除”按钮,用户可以选择多个项并一次性删除。
3、修改 ForEach 结构:为每行增加一个复选框或按钮,用户可以选择每一行。
4、批量删除逻辑:当“批量删除”按钮被点击时,删除所有选中的项。
示例代码
struct ContentView: View {
@Environment(\.modelContext) var modelContext
@Query var books: [Book]
@State private var isEditing = false // 控制编辑模式
@State private var selectedBooks = Set<Book>() // 存储选中的书籍项
func deleteSelectedBooks() {
for book in selectedBooks {
modelContext.delete(book)
}
selectedBooks.removeAll() // 清空已删除项
}
var body: some View {
NavigationStack {
List {
ForEach(books) { book in
HStack {
if isEditing {
Button(action: {
if selectedBooks.contains(book) {
selectedBooks.remove(book)
} else {
selectedBooks.insert(book)
}
}) {
Image(systemName: selectedBooks.contains(book) ? "checkmark.circle.fill" : "circle")
.foregroundColor(.blue)
}
}
NavigationLink(value: book) {
HStack {
VStack(alignment: .leading) {
Text(book.title)
.font(.headline)
Text(book.author)
.foregroundStyle(.secondary)
}
}
}
}
}
}
.navigationTitle("Bookworm")
.toolbar {
ToolbarItem(placement: .topBarLeading) {
Button(action: {
print("点击了自定义的修改按钮")
isEditing.toggle()
}, label: {
Image(systemName: "square.and.pencil")
})
}
if isEditing {
ToolbarItem(placement: .bottomBar) {
Button("Delete Selected") {
deleteSelectedBooks()
}
.disabled(selectedBooks.isEmpty) // 只有当有选中项时才能批量删除
}
}
}
}
}
}
代码解析
1、控制变量
@State private var isEditing = false // 控制编辑模式
@State private var isEditing:一个布尔值,用于控制是否处于编辑模式。在点击 EditButton 时切换 isEditing 状态。
@State private var selectedBooks = Set<Book>() // 存储选中的书籍项
@State private var selectedBooks:一个集合,用来存储用户选择的书籍。在编辑模式下,用户可以点击选择多本书。
2、列表管理
List {
ForEach(books) { book in
HStack {
if isEditing {
Button(action: {
if selectedBooks.contains(book) {
selectedBooks.remove(book)
} else {
selectedBooks.insert(book)
}
}) {
Image(systemName: selectedBooks.contains(book) ? "checkmark.circle.fill" : "circle")
.foregroundColor(.blue)
}
}
NavigationLink(value: book) {
HStack {
VStack(alignment: .leading) {
Text(book.title)
.font(.headline)
Text(book.author)
.foregroundStyle(.secondary)
}
}
}
}
}
}
在ForEach中,添加了一个点击按钮,当isEditing为true时,表示处于编辑状态下并显示对应的按钮,否则不显示。
该按钮的作用为:当点击该按钮时,检查当前元素是否存在selectedBooks数组中,如果存在则移除该元素,同时Image展示为“circle”,如果selectedBooks数组中没有该元素,则添加到数组中并将Image展示为“checkmark.circle.fill”。
3、多选按钮
在 ForEach 的 HStack 中,添加一个按钮,通过选中和取消选中书籍来更新 selectedBooks。
.toolbar {
ToolbarItem(placement: .topBarLeading) {
Button(action: {
print("点击了自定义的修改按钮")
isEditing.toggle()
}, label: {
Image(systemName: "square.and.pencil")
})
}
if isEditing {
ToolbarItem(placement: .bottomBar) {
Button("Delete Selected") {
deleteSelectedBooks()
}
.disabled(selectedBooks.isEmpty) // 只有当有选中项时才能批量删除
}
}
}
两个按钮分别位于视图的左上角和底部,当点击.topBarLeading位置的按钮时,会切换isEditing的状态,如果isEditing为true,则展示前面提到的按钮以及展示底部的“Delete Selected”按钮。
多选列表后,点击底部的“Delete Selected”按钮后,会调用deleteSelectedBooks方法。
4、删除按钮逻辑
deleteSelectedBooks 遍历 selectedBooks 集合,将其中的书籍项从 modelContext 中删除,并清空集合。
func deleteSelectedBooks() {
for book in selectedBooks {
modelContext.delete(book)
}
selectedBooks.removeAll() // 清空已删除项
}
这里之所以使用for-in方法,是因为selectedBooks数组不是单独的元素,可能在选择的过程中选择了多个元素,因此需要进行遍历删除。
列表选择中存在selectedBooks数组的逻辑:
if isEditing {
Button(action: {
if selectedBooks.contains(book) {
selectedBooks.remove(book)
} else {
selectedBooks.insert(book)
}
}) {
Image(systemName: selectedBooks.contains(book) ? "checkmark.circle.fill" : "circle")
.foregroundColor(.blue)
}
}
所以,在列表中通过对selectedBooks的添加或移除,来汇总多选并需要展示的数组元素,然后通过相关的删除方法删除元素。
modelContext.delete(book)
是SwiftData的删除方法,如果是数组元素,则可以使用
books.remove(atOffsets: book) // 从数组中删除项目
相关资料
SwiftUI删除元素的onDelete方法:https://fangjunyu.com/2024/09/27/swift-ui-ondelete%e5%87%bd%e6%95%b0/