我遇到了一个问题,即用户在一个部分中更新了一项,导致该项目被过滤并移至另一部分。发生这种情况时,行视图的内容不会总是更新。如图中所见,当项目位于同一部分时,它每次都会更新,而当它移动时,它只会每隔一次更新一次(尽管并非总是如此)。它的行为就像正在重用行视图并且提供给它的对象没有更新。我想知道是否有人遇到过这种情况并且知道该怎么办。
这是有关如何重新创建此视图的代码。
在SceneDelegate.swift
中:
let contentView = ContentView().environmentObject(ItemStore())
if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)
window.rootViewController = UIHostingController(rootView: contentView)
self.window = window
window.makeKeyAndVisible()
}
然后在ContentView.swift
中输入
import SwiftUI
struct ContentView: View {
@EnvironmentObject var itemStore: ItemStore
var body: some View {
NavigationView {
List {
Section(header: Text("Even")) {
ForEach(itemStore.itemsEven) { item in
ItemRowView(item: item) {
self.itemStore.increment(item: item)
}
}
}
Section(header: Text("Odd")) {
ForEach(itemStore.itemsOdd) { item in
ItemRowView(item: item) {
self.itemStore.increment(item: item)
}
}
}
Section(header: Text("All")) {
ForEach(itemStore.itemsAll) { item in
ItemRowView(item: item) {
self.itemStore.increment(item: item)
}
}
}
}
.listStyle(GroupedListStyle())
.navigationBarTitle("Items")
}
}
}
struct ItemRowView: View {
var item: Item
var onTap: () -> Void
var body: some View {
HStack {
Text(item.name)
Spacer()
Button(action: onTap) {
Text("\(item.count)")
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView().environmentObject(ItemStore())
}
}
struct Item: Identifiable {
var id: String
var name: String
var count: Int
var isEven: Bool {
count % 2 == 0
}
}
class ItemStore: ObservableObject {
@Published var itemsEven: [Item]
@Published var itemsOdd: [Item]
@Published var itemsAll: [Item]
init() {
let even = [
Item(id: "0", name: "Star Wars", count: 0),
]
let odd: [Item] = []
itemsEven = even
itemsOdd = odd
itemsAll = even + odd
}
func increment(item: Item) {
printItems(title: "START")
var allItems = itemsAll
let index = allItems.firstIndex(where: { $0.id == item.id })!
let storedItem = allItems[index] // avoid captured value
allItems[index].count = storedItem.count + 1
print("DURING \(allItems) \(allItems[index].count) \(item.count + 1)")
allItems.sort { $0.count < $1.count }
itemsEven = []
itemsOdd = []
allItems.forEach { item in
if item.isEven {
itemsEven.append(item)
} else {
itemsOdd.append(item)
}
}
itemsAll = allItems
printItems(title: "END")
}
func printItems(title: String) {
print("\(title) ------------")
print("All \(itemsAll)")
print("Even \(itemsEven)")
print("Odd \(itemsOdd)")
print("------------------")
}
}
可能是Why is my SwiftUI List row not always updating internally
的副本哦,第一次点击时左移的余量有点奇怪。
答案 0 :(得分:1)
这似乎已在iOS 14中修复(仅在beta 3中已尝试过,并且在更改部分后已正确更新)。对于iOS 13开发而言并不理想-似乎我们的选择是处理它或没有漂亮的更改截面动画-但至少它可以在iOS 14中使用。
答案 1 :(得分:0)
这是因为Item.id
未被更改,所以List
将该项目作为同一项目进行跟踪(由于问题或由于设计而定-未知),因此以下添加的行修复了更新(因为项目被解释为新的。)
allItems[index].count = storedItem.count + 1
allItems[index].id = UUID().uuidString // !! modified value, so update ID
当然,这种方法并非总是适用,但出于澄清的目的,我认为将其提供以供考虑是有帮助的。
答案 2 :(得分:0)
正式答案是缺少正式答案Hashable
struct Item: Identifiable, Hashable {
var id: String
var name: String
var count: Int
var isEven: Bool {
count % 2 == 0
}
}
因此,您可以通过id
方式使用列表。
Section(header: Text("Odd")) {
ForEach(itemStore.itemsOdd, id:\.self) { item in
ItemRowView(item: item ,onTap: {
self.updateItems(item)
})
}
}
答案 3 :(得分:0)
另一种解决方案是将Item设置为ObservableObject并@Publish每个值,以便将更新更新到位。假定使用结构是不可变的。