【问题标题】:SwiftUI Row in Grouped List Not Updating Properly分组列表中的 SwiftUI 行未正确更新
【发布时间】:2020-04-12 11:30:26
【问题描述】:

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

这里是如何重新创建这个视图的代码。

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重复

哦,第一次点击时左移的边距有点奇怪。

【问题讨论】:

  • 如果不关心动画,可以在List上加.id()
  • 啊,但我喜欢这个动画!
  • 您找到解决方案了吗?再多的覆盖 Hashable、Equatable、Identifiable 似乎都无法为我解决这个问题。 ID 来自其他地方,所以我不能只选择一个新 ID,我想要动画。

标签: swiftui-list swiftui


【解决方案1】:

正式答案是缺少正式答案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)
                     })
                 }
    }

【讨论】:

    【解决方案2】:

    这似乎在 iOS 14 中已修复(刚刚在 beta 3 中尝试过,并且在更改部分后正确更新)。不适合 iOS 13 开发——似乎我们的选项要么处理它,要么没有漂亮的变化部分动画——但至少它可以在 iOS 14 中工作。

    【讨论】:

      【解决方案3】:

      这是因为Item.id 没有更改,所以List 将该项目跟踪为相同项目(由于问题或设计 - 未知),因此以下添加的行修复了更新(因为项目被解释为新)。

      allItems[index].count = storedItem.count + 1
      allItems[index].id = UUID().uuidString // !! modified value, so update ID
      

      当然,这种方法并不总是合适的,但为了澄清起见,我认为提供它以供考虑会有所帮助。

      【讨论】:

      • 这可行,但它似乎是我真正想避免的解决方法。最终,我希望我不需要更改对象的标识符。我遇到这个问题的原因是我从服务器获取对象,并且根据它们的属性将它们分类到各种列表中(每次获取它们时都会改变)。我真的不想简单地给他们一个不同的标识符,以便可以正确更新列表。
      【解决方案4】:

      另一个解决方案是使 Item 成为 ObservableObject 并 @Publish 每个值,以便更新将就地更新。使用结构,它们被假定为不可变的。

      【讨论】:

      • 介意在这个答案中详细说明吗?我认为这对社区来说是很好的例子。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-08-26
      • 2019-02-08
      • 1970-01-01
      • 1970-01-01
      • 2022-06-11
      • 2020-10-25
      • 1970-01-01
      相关资源
      最近更新 更多