滚动列表中的项目时丢弃 SwiftUI 状态

Posted

技术标签:

【中文标题】滚动列表中的项目时丢弃 SwiftUI 状态【英文标题】:SwiftUI state discarded when scrolling items in a list 【发布时间】:2021-05-30 03:23:42 【问题描述】:

我对 Swift 和 SwiftUI 非常陌生,所以对于这个非常基本的问题表示歉意。我一定误解了 SwiftUI 生命周期以及它与 @State 的交互。

我有一个列表,当您单击该行时,它会增加一个计数器。如果我单击某些行项目以增加计数器,向下滚动,然后再次向上滚动 - 状态再次重置为 0。谁能指出我哪里出错了?非常感谢。

struct TestView : View 
    @State private var listItems:[String] = (0..<50).map  String($0) 
    var body: some View 
        List(listItems, id: \.self)  listItem in
            TestViewRow(item: listItem)
        
    


struct TestViewRow: View 
    var item: String
    @State private var count = 0
    
    var body: some View 
        HStack 
            Button(item, action: 
                self.count += 1
            )
            Text(String(self.count))
            Spacer()
        
    

【问题讨论】:

UITableView 非常相似。 Cells 被重复使用。您正在修改 cell,但未将值保留在 model 中。在内存中保存 50 个 TextViewRow 实例效率非常低。 @Stateproperty 包装器使属性可变并使其值保持不变,只要视图处于活动状态。 @vadian 我原以为 TextViewRow 实例会消失,但是当它们“重新水合”时会保留状态? 想象一下它必须保留List中的值和位置... @vadian 很公平,谢谢 - 有没有关于这种行为的文档?我找不到任何提及它。 【参考方案1】:

List 中的项目可能延迟加载,具体取决于操作系统(macOS 与 ios)、列表的长度、屏幕上的项目数量等。

如果加载了一个列表项,然后更改了它的状态,并且该项目自卸载/重新加载到List 中,则该状态不会重新分配给该项目。

您可以将状态移动到不会被卸载的父视图,而不是在每个 List 行上存储 @State

struct ContentView : View 
    @State private var listItems:[(item:String,count:Int)] = (0..<50).map  (item:String($0),count:0) 
    
    var body: some View 
        List(Array(listItems.enumerated()), id: \.0)  (index,item) in
            TestViewRow(item: item.item, count: $listItems[index].count)
        
    


struct TestViewRow: View 
    var item: String
    @Binding var count : Int
    
    var body: some View 
        HStack 
            Button(item, action: 
                count += 1
            )
            Text(String(count))
            Spacer()
        
    

【讨论】:

以上是关于滚动列表中的项目时丢弃 SwiftUI 状态的主要内容,如果未能解决你的问题,请参考以下文章

SwiftUI List 在任何视图更改时重置滚动

如何在 TabView SwiftUI 中保存视图的列表状态

SwiftUI - 防止列表在项目选择时将滚动位置重置为顶部

在 SwiftUI 中滚动时,许多项目列表崩溃,我该如何解决?

SwiftUI:从列表中的项目获取切换状态

滚动视图中的内容作为列表项在滚动(swiftui)时消失,为啥?