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
相关产品推荐
相关产品推荐

