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

相关推荐
_阿南_4 小时前
项目中新增给AI制定的代码规范
前端·程序员
名字还没想好☜5 小时前
React 实现暗黑模式切换:localStorage 持久化、SSR 首屏闪烁与跟随系统主题
前端·javascript·react.js·ecmascript·react·next.js
console.log('npc')6 小时前
Git 冲突与 AI 协助指南
前端·人工智能·git·大模型
爱学堂IT分享7 小时前
Cesium可视化系统实战课程-Cesium教程学习
前端
糖墨夕8 小时前
理解大语言模型:Agent 的“大脑”
前端·agent
Rain的Java大神之路8 小时前
JavaWeb开发如何解决跨域问题
java·前端·后端·nginx·web安全·面试·运维开发
cpolar技术支持8 小时前
浏览器也能跑本地 AI:用 Transformers.js + WebGPU 做一个最小推理 Demo,cpolar 给同事远程体验
前端·ai·cpolar·webgpu·transformers.js
计算机魔术师10 小时前
OpenAI 发布 GPT-6 Astra:多项基准刷新纪录, cybersecurity 能力达 Critical 阈值
前端
xy345310 小时前
Axure9.0中继器遮罩实现方法
前端·ui·html·axure·原型·产品设计
风骏时光牛马10 小时前
智能任务自动化协同AI工作流
前端