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

相关推荐
子兮曰5 天前
jev-ultrafast 深度解析:7 秒订机票的浏览器 Agent 是如何炼成的
前端·后端·agent
子兮曰5 天前
Jev 爆发一周:7 秒 Agent 背后的 System One 生态与三场争议
前端·后端·ai编程
前端小万5 天前
写公众号赚了 3000 块后,我做了一款叫 "一键成稿" 的软件
前端·微信小程序
爱勇宝5 天前
ZCode 开源 24 小时:一份没有历史的账本,回答不了"有没有偷代码"
前端·后端·chatglm (智谱)
三十而立洋5 天前
Cookie 详解:从产生到安全,一次讲透
前端·javascript
卡布鲁5 天前
把一个 Vite + Vue3 应用塞进 qiankun (React + Umi3) 主站:十个坑的复盘
前端·javascript·react.js
汉堡大王95275 天前
Jev:不是聊天机器人, 而是一个智能 if 语句
前端·人工智能·后端
梦想很大很大5 天前
从运行事实到回归证据:Workrun 的 Telemetry 与 Evaluation 实践
前端·人工智能·后端
计算机魔术师5 天前
Meta Muse agent 接入 Shopify 的 Shop Pay 实现代理式购物
前端
沙蒿同学5 天前
我用 Go 搭了一条 AI Agent 流水线:从 1 张商品图到一整套淘宝详情页
前端·javascript·后端