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

相关推荐
不爱吃饭爱吃菜32 分钟前
uniapp微信小程序-长按按钮百度语音识别回显文字
前端·javascript·vue.js·百度·微信小程序·uni-app·语音识别
程序员拂雨1 小时前
Angular 知识框架
前端·javascript·angular.js
GoodStudyAndDayDayUp2 小时前
gitlab+portainer 实现Ruoyi Vue前端CI/CD
前端·vue.js·gitlab
程序员阿明2 小时前
vite运行只能访问localhost解决办法
前端·vue
前端 贾公子2 小时前
uniapp -- 验证码倒计时按钮组件
前端·vue.js·uni-app
淡笑沐白2 小时前
AJAX技术全解析:从基础到最佳实践
前端·ajax
龙正哲2 小时前
如何在Firefox火狐浏览器里-安装梦精灵AI提示词管理工具
前端·firefox
徐徐同学3 小时前
轻量级Web画板Paint Board如何本地部署与随时随地在线绘画分享
前端
LuckyLay3 小时前
Vue百日学习计划Day4-8——Gemini版
前端·vue.js·学习
八戒社3 小时前
WooCommerce短代码Shortcodes使用方法
前端·wordpress·woocommerce