如何解决更新和删除列表中的项目,SwiftUI
在待办事项列表中,更新列表有两个问题。
- 此外,也许您知道如何在没有更多项目时隐藏部分,并在添加任务时重新出现。
列表视图
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",Itemviewmodel(item: Item(productName: "3",Itemviewmodel(item: Item(productName: "4",Itemviewmodel(item: Item(productName: "5",Itemviewmodel(item: Item(productName: "6",accomplished: true)),Itemviewmodel(item: Item(productName: "7",Itemviewmodel(item: Item(productName: "8",Itemviewmodel(item: Item(productName: "9",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
}
单元格的可重用视图
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()
},height: 25)
})
}.opacity(itemCellviewmodel.item.accomplished ? 0.3 : 1)
}
}
Itemviewmodel
import Foundation
import Combine
class Itemviewmodel: ObservableObject,on: self)
.store(in: &cancellables) // <- for memory management purposes
}
}
Listviewmodel
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)
}
}
注意:第一部分不需要两个 .onDelete。
注意: 如果您仍然面临动画问题,请使用主队列。
.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.
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。