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

SwiftUI中使用List+@FocusState监听焦点时如何避免滚动频繁重建?

解决SwiftUI List滚动时因@FocusState监听导致的频繁重建问题

问题分析

你遇到的核心问题是:使用List配合@FocusState跟踪焦点,并通过.onChange(of: focus)实现聚焦输入框的滚动定位时,List滚动会触发ContentView频繁重建,复杂内容下卡顿明显。排查可知,即使.onChange闭包未打印内容(仅手动聚焦时触发),滚动过程中_focus仍会被标记为状态变化,进而引发视图重建。

解决方案

核心思路是只监听焦点的实际变化事件,规避SwiftUI内部状态波动导致的不必要重建。可以用@FocusState的publisher结合.onReceive替代.onChange,具体实现如下:

修改后的代码

struct ContentView: View {
    enum Field: Hashable {
        case fieldId(Int)
    }
    
    @FocusState var focus: Field?
    @State var text: String = ""
    
    var body: some View {
        List {
            let _ = Self._printChanges()
            ForEach(0..<100) { index in
                TextField("输入第\(index)项内容", text: $text)
                    .id(Field.fieldId(index))
                    .focused($focus, equals: .fieldId(index))
            }
        }
        // 替换原.onChange,通过publisher监听焦点实际变化
        .onReceive(focus.publisher) { newFocus in
            guard newFocus != nil else { return }
            print("仅在焦点实际变化时触发")
            // 此处添加ScrollViewReader滚动逻辑,示例:
            // withAnimation {
            //     scrollProxy.scrollTo(newFocus, anchor: .center)
            // }
        }
    }
}

原理说明

  • focus.publisher仅在focus的值真正发生改变时发送事件,不会响应List滚动时SwiftUI内部的临时焦点调整操作。
  • 相比.onChange(of: focus),这种方式不会因无关的状态标记触发视图重建,同时完整保留了监听焦点变化的能力,且不影响List的分组、编辑(删除/移动项)等原有功能。

额外优化建议

若列表项为复杂视图,可将TextField封装为独立子视图,进一步隔离状态变化,缩小重建范围:

struct FocusTextField: View {
    let index: Int
    @Binding var text: String
    @FocusState.Binding var isFocused: Bool
    
    var body: some View {
        TextField("输入第\(index)项内容", text: $text)
            .focused($isFocused)
    }
}

// 在ContentView中调用:
ForEach(0..<100) { index in
    FocusTextField(
        index: index,
        text: $text,
        isFocused: $focus,
        equals: .fieldId(index)
    )
    .id(Field.fieldId(index))
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 23:06:27