onPreferenceChange的基本用法

SwiftUI 中的 onPreferenceChange 修饰符

onPreferenceChange 是 SwiftUI 中一个强大的工具,用于响应视图层次结构中传递的偏好值(preference)变化。它通常与 PreferenceKey 协议一起使用,实现父子视图之间的数据传递。

基本概念

onPreferenceChange 允许你在偏好值发生变化时执行某些操作,而不需要显式的数据绑定或状态管理。

基本用法

swift 复制代码
struct MyView: View {
    @State private var childSize: CGSize = .zero
    
    var body: some View {
        VStack {
            Text("子视图尺寸: \(childSize.width) x \(childSize.height)")
            
            ChildView()
                .onPreferenceChange(SizePreferenceKey.self) { newSize in
                    childSize = newSize
                }
        }
    }
}

struct ChildView: View {
    var body: some View {
        Rectangle()
            .fill(Color.blue)
            .frame(width: 100, height: 100)
            .preference(key: SizePreferenceKey.self, value: CGSize(width: 100, height: 100))
    }
}

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

实际应用场景

1. 获取视图尺寸

swift 复制代码
struct ContentView: View {
    @State private var textSize: CGSize = .zero
    
    var body: some View {
        VStack {
            Text("Hello, World!")
                .background(GeometryReader { geometry in
                    Color.clear
                        .preference(key: TextSizeKey.self, value: geometry.size)
                })
            
            Text("文本尺寸: \(textSize.width) x \(textSize.height)")
        }
        .onPreferenceChange(TextSizeKey.self) { size in
            textSize = size
        }
    }
}

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

2. 多个视图的偏好值聚合

swift 复制代码
struct ContentView: View {
    @State private viewRects: [CGRect] = []
    
    var body: some View {
        VStack {
            ForEach(0..<5) { index in
                Text("Item \(index)")
                    .padding()
                    .background(GeometryReader { geometry in
                        Color.clear
                            .preference(key: ViewRectsKey.self, 
                                       value: [geometry.frame(in: .global)])
                    })
            }
        }
        .onPreferenceChange(ViewRectsKey.self) { rects in
            viewRects = rects
        }
    }
}

struct ViewRectsKey: PreferenceKey {
    static var defaultValue: [CGRect] = []
    static func reduce(value: inout [CGRect], nextValue: () -> [CGRect]) {
        value.append(contentsOf: nextValue())
    }
}

与 GeometryReader 结合使用

onPreferenceChange 经常与 GeometryReader 结合使用来获取视图的几何信息:

swift 复制代码
struct ScrollViewOffsetPreferenceKey: PreferenceKey {
    static var defaultValue: CGFloat = 0
    static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
        value = nextValue()
    }
}

struct ScrollViewWithOffset: View {
    @State private var offset: CGFloat = 0
    
    var body: some View {
        ScrollView {
            VStack {
                ForEach(0..<50) { i in
                    Text("Item \(i)")
                        .frame(maxWidth: .infinity)
                        .padding()
                }
            }
            .background(GeometryReader { geometry in
                Color.clear
                    .preference(
                        key: ScrollViewOffsetPreferenceKey.self,
                        value: geometry.frame(in: .named("scrollView")).minY
                    )
            })
        }
        .coordinateSpace(name: "scrollView")
        .onPreferenceChange(ScrollViewOffsetPreferenceKey.self) { value in
            offset = value
        }
        .overlay(
            Text("当前偏移: \(offset)")
                .padding(),
            alignment: .bottom
        )
    }
}

注意事项

  1. 性能考虑:偏好值变化会触发视图更新,应避免频繁更新导致性能问题

  2. PreferenceKey 的 reduce 方法:用于合并多个视图的偏好值,需要根据需求正确实现

  3. 使用场景:适合用于父视图需要收集子视图信息的场景,但不适合用于频繁更新的数据

  4. 与 onChange 的区别onPreferenceChange 专门用于处理偏好值变化,而 onChange 用于观察任意状态变化

onPreferenceChange 提供了一种声明式的方式来响应视图层次结构中的数据变化,是实现复杂布局和交互的强大工具。

相关推荐
PasserbyX9 分钟前
ES6 WeakMap 生效的证明: FinalizationRegistry
前端·javascript
努力学习的小刘12 分钟前
如何使用react-router实现动态路由
前端·javascript
PasserbyX12 分钟前
JS原型链
前端·javascript
curdcv_po12 分钟前
你知道Cookie的弊端吗?
前端
curdcv_po14 分钟前
前端CSS高频面试题详解
前端
Danta18 分钟前
从0开始学习three.js(1)😁
前端·javascript·three.js
我的心巴18 分钟前
vue-print-nb 打印相关问题
前端·vue.js·elementui
coderYYY38 分钟前
element树结构el-tree,默认选中当前setCurrentKey无效
前端·javascript·vue.js
GISer_Jing1 小时前
[总结篇]个人网站
前端·javascript
疯狂的沙粒1 小时前
在web-view 加载的本地及远程HTML中调用uniapp的API及网页和vue页面是如何通讯的?
前端·uni-app·html