分组列表中的SwiftUI行未正确更新

时间:2019-12-19 21:17:33

标签: swiftui swiftui-list swiftui-bug

Odd SwiftUI

我遇到了一个问题,即用户在一个部分中更新了一项,导致该项目被过滤并移至另一部分。发生这种情况时,行视图的内容不会总是更新。如图中所见,当项目位于同一部分时,它每次都会更新,而当它移动时,它只会每隔一次更新一次(尽管并非总是如此)。它的行为就像正在重用行视图并且提供给它的对象没有更新。我想知道是否有人遇到过这种情况并且知道该怎么办。

这是有关如何重新创建此视图的代码。

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

的副本

哦,第一次点击时左移的余量有点奇怪。

4 个答案:

答案 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每个值,以便将更新更新到位。假定使用结构是不可变的。