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

SwiftUI PreferenceKey不更新求助:滚动时onPreferenceChange无响应

解决PreferenceKey滚动时offsetCGSize不更新的问题

问题分析

你的代码中,onPreferenceChange仅在启动时触发一次,滚动时offsetCGSize不更新的核心原因是:

  • 绑定preference的TextEditor本身未发生布局尺寸变化,SwiftUI不会主动重新计算并传递preference值。
  • 虽然Overlay中的文本能显示最新的minY,但这是ScrollView滚动时Overlay随TextEditor重绘,被动读取了GeometryReader的当前值,preference的传递机制并未被触发。

修复方案

将preference绑定到TextEditor的背景视图上,利用背景视图随滚动实时更新位置的特性,触发preference的重新计算和传递。

修改后的完整代码:

struct SOView: View {

    @State private var firstText: String = "first"
    @State private var secondText: String = "second"
    @State private var thirdText: String = "third"

    @State private var offsetCGSize: CGSize = .zero

    var body: some View {
        ScrollView {
            VStack {
                
                ForEach(0..<20) {index in
                    Text("Line number \(index)")
                }
                
                Image(systemName: "globe")
                    .imageScale(.large)
                    .foregroundColor(.accentColor)
                TextField("First", text: $firstText)
                
                GeometryReader { teGeo in
                    TextEditor(text: $secondText)
                        .frame(height: 100)
                        .overlay(
                            RoundedRectangle(cornerRadius: 10).stroke(lineWidth: 2)
                        )
                        .overlay(
                            Text("\(teGeo.frame(in: .global).minY)")
                        )
                        // 将preference移至背景视图,触发滚动时的更新
                        .background(
                            Color.clear
                                .preference(key: OffsetPreferenceKey.self, value: CGSize(width: 0, height: teGeo.frame(in: .global).minY))
                        )
                }
                .frame(height: 100)
                
                TextField("Third", text: $thirdText)
                Text("TextEditor top is \(offsetCGSize.height)")
                
            }
            .padding()
        
        }
        .onPreferenceChange(OffsetPreferenceKey.self) { value in
            offsetCGSize = value
            print("offsetCGSize is \(offsetCGSize)")
        }

    }
}

private struct OffsetPreferenceKey: PreferenceKey {
    static var defaultValue: CGSize = .zero
    static func reduce(value: inout CGSize, nextValue: () -> CGSize) {
        value = nextValue()
    }
}

原理说明

  • Color.clear作为背景视图,会完全贴合TextEditor的布局范围,当ScrollView滚动时,背景视图的全局位置会实时变化,触发SwiftUI重新计算preference值。
  • 这种方式利用了SwiftUI的布局更新机制:背景视图的位置变化属于布局属性变更,会强制触发preference的传递流程,从而让onPreferenceChange实时收到最新的偏移值。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 15:25:32