You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SwiftUI长列表含TextField与Button滚动时内容丢失问题咨询

问题分析与解决方案

这绝对不是SwiftUI的Bug,是咱们实现的时候踩了几个SwiftUI列表状态管理的常见坑,我来给你拆解清楚:

核心问题出在哪?

  1. 状态传递方式错误:你在ContentView里定义的bindOne到bindSix是独立的@State变量,传递给Row的时候是值传递,而Row里又用@State存储了本地副本——这意味着Row里的TextField修改的只是自己本地的字符串,和ContentView里的源数据完全没关联。
  2. List的视图复用机制:SwiftUI的List会在滚动时复用行视图,当行被滚出屏幕再滚回来时,原来的Row实例会被重新初始化,本地的@State状态会被重置回初始值,就出现了“内容被清空”的现象。
  3. 行状态管理分散:每个行需要独立的文本输入状态和按钮状态,但你把这些状态拆成了一堆单独的变量,既不好维护,也没法和列表的动态复用适配。

修正后的实现方案

我们需要把每个行的状态封装成一个统一的模型,用数组管理所有行的数据,再通过@Binding把数据源和行视图绑定起来,这样就能保证滚动时状态不会丢失。

第一步:定义行数据模型

先创建一个结构体来存储每行的两个输入文本和按钮状态:

struct RowData: Identifiable {
    let id = UUID()
    var text1: String
    var text2: String
    var isButtonOn: Bool
}

用Identifiable是为了让ForEach能正确识别每个行元素。

第二步:重构ContentView

把原来分散的@State变量换成一个数组,用ForEach遍历数组并传递绑定:

struct ContentView: View {
    @State private var rows: [RowData] = [
        RowData(text1: "one", text2: "one", isButtonOn: false),
        RowData(text1: "two", text2: "two", isButtonOn: false),
        RowData(text1: "three", text2: "three", isButtonOn: false),
        RowData(text1: "four", text2: "four", isButtonOn: false),
        RowData(text1: "five", text2: "five", isButtonOn: false),
        RowData(text1: "six", text2: "six", isButtonOn: false)
    ]
    
    var body: some View {
        VStack{
            Text("test") 
                .font(.largeTitle) 
            List{
                ForEach($rows) { $row in
                    Row(data: $row)
                }
            } 
        } 
    } 
}

第三步:重构Row视图

把原来的@State换成@Binding,直接绑定到RowData模型的属性:

struct Row: View {
    @Binding var data: RowData
    
    var body: some View {
        HStack{
            TextField("输入文本1", text: $data.text1) 
                .font(.largeTitle) 
            TextField("输入文本2", text: $data.text2) 
                .font(.largeTitle) 
            Button(action: {
                data.isButtonOn.toggle()
            }){
                Text(data.isButtonOn ? "Yes" : "No")
                    .font(.largeTitle) 
            } 
        } 
        .padding(.vertical, 70) 
    } 
}

为什么这样就能解决问题?

  • 绑定同步状态:通过@Binding,Row里的TextField和Button直接修改的是ContentView里的rows数组中的数据,所有状态都统一存在数据源里,不会因为视图复用丢失。
  • 数组管理动态列表:用数组存储所有行数据,不仅适配List的复用机制,后续要添加/删除行也会非常方便。
  • 明确的数据源单一性:所有状态都集中在ContentView的rows变量里,符合SwiftUI的“单一数据源”原则,避免状态分散导致的混乱。

内容的提问来源于stack exchange,提问作者jlammering

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 08:42:11