Listview 更新和删除列表中的项目,SwiftUI
在待办事项列表中,我在更新列表时遇到两个问题Listview 更新和删除列表中的项目,SwiftUI,listview,swiftui,swiftui-list,Listview,Swiftui,Swiftui List,在待办事项列表中,我在更新列表时遇到两个问题 删除列表中的项目时,它会飞到随机位置。(油嘴滑舌)。我想这是关于使用uniqueidentifier 勾选列表中的项目时,不会更改节。视图仅在添加新项目后更新 此外,也许您知道如何在不再有项目时隐藏分区,并在添加任务时重新显示 列表视图 struct ContentView: View { @ObservedObject var listViewModel = ListViewModel() @Stat
identifier
- 此外,也许您知道如何在不再有项目时隐藏分区,并在添加任务时重新显示
struct ContentView: View {
@ObservedObject var listViewModel = ListViewModel()
@State var newItem = ""
var body: some View {
VStack {
Form {
// To-do section
Section(header: Text("New")) {
ForEach(listViewModel.itemCellViewModels) { itemCellViewModel in
if itemCellViewModel.item.accomplished == false {
ItemCellView(itemCellViewModel: itemCellViewModel)
}
}.onDelete(perform: { indexSet in
//remove item from the shopping list
self.listViewModel.itemCellViewModels.remove(atOffsets: indexSet)
print(indexSet)
})
.onDelete(perform: listViewModel.removeRows)
}
// Accomplished section
Section(header: Text("Done")) {
ForEach(listViewModel.itemCellViewModels.indices, id: \.self) { index in
if listViewModel.itemCellViewModels[index].item.accomplished == true {
ItemCellView(itemCellViewModel: listViewModel.itemCellViewModels[index])
}
}.onDelete(perform: listViewModel.removeRows)
}
}
TextField("Enter item here", text: $newItem) { _ in
} onCommit: {
self.listViewModel.addItem(item: Item(productName: newItem, accomplished: false))
newItem = ""
}
.autocapitalization(.none)
.padding()
.border(Color.blue)
.padding()
}
// update ViewModel with placeholder data
.onAppear(perform: {self.listViewModel.itemCellViewModels.append(contentsOf: placeholderItems)})
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
var placeholderItems = [
ItemViewModel(item: Item(productName: "1", accomplished: false)),
ItemViewModel(item: Item(productName: "2", accomplished: false)),
ItemViewModel(item: Item(productName: "3", accomplished: false)),
ItemViewModel(item: Item(productName: "4", accomplished: false)),
ItemViewModel(item: Item(productName: "5", accomplished: false)),
ItemViewModel(item: Item(productName: "6", accomplished: true)),
ItemViewModel(item: Item(productName: "7", accomplished: true)),
ItemViewModel(item: Item(productName: "8", accomplished: true)),
ItemViewModel(item: Item(productName: "9", accomplished: true)),
ItemViewModel(item: Item(productName: "10", accomplished: true))
]
/// The rest of the code
// Reusable View for Cells
struct ItemCellView: View {
@ObservedObject var itemCellViewModel: ItemViewModel
// send an item (doesn't return anything)
var onCommit: (Item) -> (Void) = { _ in }
var body: some View {
HStack {
VStack(alignment: .leading) {
Text(itemCellViewModel.item.productName)
}
Spacer()
Button(action: {
itemCellViewModel.item.accomplished.toggle()
}, label: {
Image(systemName: (itemCellViewModel.item.accomplished ? "checkmark.square" : "square"))
.resizable()
.frame(width: 25, height: 25)
})
}.opacity(itemCellViewModel.item.accomplished ? 0.3 : 1)
}
}
import Foundation
import Combine
class ItemViewModel: ObservableObject, Identifiable {
@Published var item: Item
var id = UUID()
@Published var items: [Item] = []
@Published var completionStateIconName = ""
private var cancellables = Set<AnyCancellable>()
init(item: Item) {
self.item = item
$item
.map { item in
item.accomplished ? "checkmark.square" : "square"
}
.assign(to: \.completionStateIconName, on: self)
.store(in: &cancellables) // <- for memory management purposes
}
}
class ListViewModel: ObservableObject {
@Published var itemCellViewModels = [ItemViewModel]()
private var cancellables = Set<AnyCancellable>()
func addItem(item: Item) {
let itemVM = ItemViewModel(item: item)
self.itemCellViewModels.append(itemVM)
}
func removeRows(at offsets: IndexSet) {
itemCellViewModels.remove(atOffsets: offsets)
}
}
// Model
struct Item: Identifiable {
var id = UUID()
var productName: String
var accomplished: Bool
}
ItemViewModel
import Foundation
import Combine
class ItemViewModel: ObservableObject, Identifiable {
@Published var item: Item
var id = UUID()
@Published var items: [Item] = []
@Published var completionStateIconName = ""
private var cancellables = Set<AnyCancellable>()
init(item: Item) {
self.item = item
$item
.map { item in
item.accomplished ? "checkmark.square" : "square"
}
.assign(to: \.completionStateIconName, on: self)
.store(in: &cancellables) // <- for memory management purposes
}
}
import Foundation
import Combine
class ListViewModel: ObservableObject {
@Published var itemCellViewModels = [ItemViewModel]()
private var cancellables = Set<AnyCancellable>()
func addItem(item: Item) {
let itemVM = ItemViewModel(item: item)
self.itemCellViewModels.append(itemVM)
}
func removeRows(at offsets: IndexSet) {
itemCellViewModels.remove(atOffsets: offsets)
}
}
struct Item: Identifiable {
var id = UUID()
var productName: String
var accomplished: Bool
}
首先,您添加了错误的
ForEach
。
因为您只需要添加那些已完成或未完成的对象。您需要使用过滤器,而不是迭代所有对象并检查ForEach中的条件。通过删除此选项,您的第一个问题将解决动画问题,您还可以添加一个空条件,以便在完成的阵列为空时,该部分会自动将其从视图中删除
作为下面的演示,我们对两个部分使用了不同的数组,因此节行的索引将发生变化,并且您的数据源是单个数组。为此,需要按id从数组中删除该项
struct ContentView: View {
@ObservedObject var listViewModel = ListViewModel()
var arrNotAccomplished: [ItemViewModel] {
return listViewModel.itemCellViewModels.filter({!$0.item.accomplished})
}
var arrAccomplished: [ItemViewModel] {
return listViewModel.itemCellViewModels.filter({$0.item.accomplished})
}
@State var newItem = ""
var body: some View {
VStack {
Form {
// To-do section
if !arrNotAccomplished.isEmpty {
Section(header: Text("New")) {
ForEach(arrNotAccomplished) { itemCellViewModel in
ItemCellView(itemCellViewModel: itemCellViewModel) { (_) -> (Void) in
self.listViewModel.objectWillChange.send()
}
}.onDelete { (index) in
guard let firstIndex = index.first else { return }
self.listViewModel.removeRows(for: arrNotAccomplished[firstIndex].id)
}
}
}
// Accomplished section
if !arrAccomplished.isEmpty {
Section(header: Text("Done")) {
ForEach(arrAccomplished) { itemCellViewModel in
ItemCellView(itemCellViewModel: itemCellViewModel) { (_) -> (Void) in
self.listViewModel.objectWillChange.send()
}
}.onDelete { (index) in
guard let firstIndex = index.first else { return }
self.listViewModel.removeRows(for: arrAccomplished[firstIndex].id)
}
}
}
}
// ====== other body view code =========
更新了删除函数代码
func removeRows(for id: UUID) {
self.itemCellViewModels.removeAll(where: {$0.id == id})
}
问题2: 当更新内部数组中的数据时,嵌套数组不会影响某些时间。 我使用了提交闭包,并将操作强制发送到主父视图和更新视图
struct ItemCellView: View {
@ObservedObject var itemCellViewModel: ItemViewModel
// send an item (doesn't return anything)
var onCommit: (Item) -> (Void) = { _ in }
var body: some View {
HStack {
VStack(alignment: .leading) {
Text(itemCellViewModel.item.productName)
}
Spacer()
Button(action: {
itemCellViewModel.item.accomplished.toggle()
onCommit(itemCellViewModel.item) //<-- Here
}, label: {
Image(systemName: (itemCellViewModel.item.accomplished ? "checkmark.square" : "square"))
.resizable()
.frame(width: 25, height: 25)
})
}.opacity(itemCellViewModel.item.accomplished ? 0.3 : 1)
}
}
为了提高可读性,您会将
productName
更改为什么?mb项目。
<代码>值/名称
/说明
?我建议名称
谢谢!添加了withAnimation{onCommit(itemCellViewModel.item)}
@MikeMaus这个答案有什么问题?删除单元格仍然是一个问题,例如,尝试删除第7行,它会删除其他元素。@MikeMaus我忽略了这一点。请检查更新的Answare。它工作得很好!我以前尝试过以下ForEach(arrnotaccompleted,id:\.self.item.id)
认为它应该通过密钥路径中提到的id删除。这没有成功。
struct ItemCellView: View {
@ObservedObject var itemCellViewModel: ItemViewModel
// send an item (doesn't return anything)
var onCommit: (Item) -> (Void) = { _ in }
var body: some View {
HStack {
VStack(alignment: .leading) {
Text(itemCellViewModel.item.productName)
}
Spacer()
Button(action: {
itemCellViewModel.item.accomplished.toggle()
onCommit(itemCellViewModel.item) //<-- Here
}, label: {
Image(systemName: (itemCellViewModel.item.accomplished ? "checkmark.square" : "square"))
.resizable()
.frame(width: 25, height: 25)
})
}.opacity(itemCellViewModel.item.accomplished ? 0.3 : 1)
}
}
.onDelete(perform: { (indexSet) in
DispatchQueue.main.async {
listViewModel.removeRows(at: indexSet)
}
})
Even in your code delete the item from the array with some delay it will work.