SwiftUI如何在运行时内容更新后获取ScrollView的内容高度
问题根因
原来的实现依赖onAppear回调获取高度,而onAppear仅会在视图首次加载完成时触发一次。当你后续更新countValue导致ForEach渲染的子元素数量变化、ScrollView内容高度变更时,背景的Color.clear视图已经完成加载,不会重新触发onAppear回调,因此无法拿到更新后的高度。
正确实现方案
使用SwiftUI官方提供的PreferenceKey协议监听内容尺寸变化,是最稳定的跨层级传递视图尺寸的方案,完整实现代码如下:
// 自定义PreferenceKey,用来传递ScrollView内容高度 private struct ScrollContentHeightKey: PreferenceKey { static var defaultValue: CGFloat = 0 static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) { // 取最新的高度值即可 value = nextValue() } } struct ContentCountView: View { @State var countValue : Int = 100 var body: some View { ScrollView { ForEach(0..<countValue, id: \.self) { i in HStack{ Text("\(i)") Button("update"){ countValue = 150 } } } .background( GeometryReader { proxy in // 读取当前内容的高度,通过PreferenceKey传递出去 Color.clear.preference(key: ScrollContentHeightKey.self, value: proxy.size.height) } ) } // 监听内容高度的变化,只要高度更新就触发回调 .onPreferenceChange(ScrollContentHeightKey.self) { newHeight in print("最新ScrollView内容高度: \(newHeight)") } } }
实现逻辑说明
- 自定义遵循
PreferenceKey协议的结构体,作为传递高度的载体,设置默认高度为0,合并规则取最新的高度值即可 - 给ScrollView的内容根视图(即
ForEach整体)添加背景的GeometryReader,实时读取内容的实际高度,通过preference修饰符把高度值传递到视图层级上游 - 在ScrollView外层添加
onPreferenceChange修饰符,监听自定义PreferenceKey的数值变化,无论是首次加载、还是countValue更新导致内容高度变化,都会触发回调拿到最新的内容高度 - 如果需要避免高频触发(比如列表滚动时动态加载大量元素),可以在回调里添加防抖逻辑,减少不必要的高度处理
内容的提问来源于stack exchange,提问作者Maharajan S
相关产品推荐
相关产品推荐

