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

相关推荐
程序员爱钓鱼3 分钟前
Node.js 编程实战:测试与调试 —— 日志与监控方案
前端·后端·node.js
Mapmost12 分钟前
数字孪生项目效率翻倍!AI技术实测与场景验证实录
前端
小酒星小杜16 分钟前
在AI时代,技术人应该每天都要花两小时来构建一个自身的构建系统-Input篇
前端·程序员·架构
Cache技术分享24 分钟前
290. Java Stream API - 从文本文件的行创建 Stream
前端·后端
陈_杨26 分钟前
前端成功转鸿蒙开发者真实案例,教大家如何开发鸿蒙APP--ArkTS 卡片开发完全指南
前端·harmonyos
小杨同学4933 分钟前
C 语言实战:枚举类型实现数字转星期(输入 1~7 对应星期几)
前端·后端
陈_杨34 分钟前
前端成功转鸿蒙开发者真实案例,教大家如何开发鸿蒙APP--ArkTS 卡片刷新机制
前端·harmonyos
go_caipu42 分钟前
Vben Admin管理系统集成qiankun微服务(二)
前端·javascript
唐叔在学习1 小时前
insertAdjacentHTML踩坑实录:AI没搞定的问题,我给搞定啦
前端·javascript·html
超绝大帅哥1 小时前
Promise为什么比回调函数更好
前端