SwiftUI中Preference的理解与使用(ScrollView偏移量示例)

在 SwiftUI 中,Preference用于从视图层次结构的较深层次向上传递信息到较浅层次。这通常用于在父视图中获取子视图的属性或状态,而不需要使用状态管理工具如@State@ObservableObjectPreference特别用于自定义布局或组件,其中子视图需要向父视图报告其尺寸或其他属性。

如何使用 Preference

使用Preference通常涉及以下几个步骤:

1. 定义PreferenceKey

创建一个遵循PreferenceKey协议的结构体。这个结构体定义了如何合并来自多个视图的值。

struct WidthPreferenceKey: PreferenceKey {
  static var defaultValue: CGFloat? = nil  // 默认值

  static func reduce(value: inout CGFloat?, nextValue: () -> CGFloat?) {
    value = value ?? nextValue()
  }
}

defaultValue
defaultValuePreferenceKey协议中必须提供的属性。它定义了存储在preferences中的数据类型的初始值。这个值是在数据类型的上下文中使用的默认值,用于在没有实际值可用时提供一个基线值。

reduce(value:nextValue:)
reduce方法是PreferenceKey协议中必须实现的方法。这个方法定义了如何将新的值合并到现有的值中。当从子视图向上传递多个值时,reduce方法会被调用来决定最终存储的值。

  • value: 这是一个inout参数,表示当前累积的preference值。
  • nextValue: 这是一个闭包,当调用时,它返回当前视图提供的新值。

reduce方法的实现取决于你的具体需求。例如,如果你想要从多个子视图中获取最大的值,你可以实现reduce方法来比较当前值和新值,并存储较大的那个。

比如下面这个例子中,defaultValue被设置为0,这是一个合理的起始值,因为我们正在寻找最大宽度。reduce方法通过比较已经累积的最大宽度和新的宽度值,并保留较大的那个,来更新这个值。

struct MaxWidthPreferenceKey: PreferenceKey {
    static var defaultValue: CGFloat = 0  // 默认值为0

    static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
        value = max(value, nextValue())  // 取最大值
    }
}

2. 设置 Preference:

在子视图中,使用.preference(key:value:)修饰符来设置其preference值。

  var body: some View {
    Text("Hello, World!")
      .background(GeometryReader { geometry in
        Color.clear
          .preference(key: WidthPreferenceKey.self, value: geometry.size.width)
      })
  }

3. 读取 Preference:
在父视图中,使用.onPreferenceChange(_:perform:)修饰符来读取preference值。

  var body: some View {
    VStack {
      Text("Hello, World!")
        .background(GeometryReader { geometry in
          Color.clear
            .preference(key: WidthPreferenceKey.self, value: geometry.size.width)
      })
    }
    .onPreferenceChange(WidthPreferenceKey.self) { width in
        if let width = width {
            print("Received width: \(width)")
        }
    }
  }

在这个例子中,Text视图的宽度被动态设置为与其内容的宽度相匹配。

计算ScrollView的偏移量offsetY

有的时候我们需要在上下滚动ScrollView的时候实时知道offset,从而做一些其他逻辑等。下面的代码就采用了Preference实现这个功能,一起来看看吧。先把代码上全。

struct PreferenceDemo: View {
  @Namespace var scrollViewSpace

  var body: some View {
    ScrollView(.vertical, showsIndicators: false) {
      VStack {
        ForEach(0..<100) { index in
          Text("This is item \(index)")
            .font(.headline)
            .frame(height: 100)
            .frame(maxWidth: .infinity)
            .background(Color.white)
            .cornerRadius(10)
            .shadow(radius: 10)
            .padding()
            .id(index)
        }

      }
      .background(GeometryReader {
          Color.clear.preference(key: ScrollOffsetPreferenceKey.self, value: -$0.frame(in: .named(scrollViewSpace)).minY)
      })
      .onPreferenceChange(ScrollOffsetPreferenceKey.self) { value in
        print("offSetY: \(value)")
      }
    }
    .coordinateSpace(name: scrollViewSpace)
  }
}

private struct ScrollOffsetPreferenceKey: PreferenceKey {
  static var defaultValue = CGFloat.zero
  static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
    value += nextValue()
  }
}

上面的实现就是按照文中说的三部曲,自定义的ScrollOffsetPreferenceKey中,将每次进来的值进行叠加。

在设置.preference(key:value:)修饰符的地方,我们传入的当前的geometryminY值,这个minY值是自定义坐标空间的值,注意我们设置了.coordinateSpace(name: scrollViewSpace)ScrollView,关于坐标系这块可以参考这篇文章。

这里得到minY后,我们在前面加了一个负号,因为在坐标系中向上滑动得到的是负值,我们人为再转一下,对于偏移量,一般向上为正,向下为负,或者向左为正,向右为负,不过这也不是决定的。

.onPreferenceChange修饰符中拿个偏移量后,就可以进行下一步的操作了。

效果如下:
在这里插入图片描述

写在最后

PreferenceSwiftUI中是一个强大的工具,允许开发者在视图层次结构中向上传递数据,而不需要复杂的状态管理。这对于创建高度可定制的UI组件非常有用,尤其是在布局和尺寸调整方面。

最后,希望能够帮助到有需要的朋友,如果觉得有帮助,还望点个赞,添加个关注,笔者也会不断地努力,写出更多更好用的文章。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/692230.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

构建智能汽车新质生产力丨美格智能亮相2024高通汽车技术与合作峰会

近日&#xff0c;以“我们一起&#xff0c;驭风前行”为主题的2024高通汽车技术与合作峰会在无锡国际会议中心隆重举行。作为高通公司的战略合作伙伴&#xff0c;美格智能受邀全程参与此次汽车技术与合作峰会。在峰会现场&#xff0c;美格智能产品团队隆重展示了多款基于高通平…

【Web API DOM07】事件委托

一&#xff1a;事件委托详解 1 什么是事件委托 利用事件流的特征&#xff08;事件冒泡&#xff09;&#xff0c;解决开发需求的知识技巧 2 事件委托好处 要真正执行任务的元素不注册事件&#xff0c;将对应的事件注册给祖先元素。减少事件的注册次数&#xff0c;提高程序运…

理解JVM内存模型与Java内存模型(JMM)

理解JVM内存模型与Java内存模型&#xff08;JMM&#xff09; 在Java程序的运行过程中&#xff0c;内存管理和线程的同步是两个重要的概念。本文将深入探讨JVM内存模型&#xff08;Java Virtual Machine Memory Model&#xff09;和JMM&#xff08;Java Memory Model&#xff0…

04 uboot 编译与调试

新手不需要详细掌握 uboot,只需要知道它是一个什么东西即可,工作中也只是改一些参数而已。 1、uboot 是什么 Linux 系统要启动就必须需要一个 bootloader 程序,也就说芯片上电以后先运行一段 bootloader 程序。这段 bootloader 程序会先初始化 DDR 等外设,然后将 Linux 内…

【C语言】10.C语言指针(5)

文章目录 1.sizeof和strlen的对比1.1 sizeof1.2 strlen1.3 sizeof 和 strlen的对⽐ 2.数组和指针笔试题解析2.1 ⼀维数组2.2 字符数组2.3 ⼆维数组 3.指针运算笔试题解析3.1 题⽬13.2 题⽬23.3 题⽬33.4 题⽬43.5 题⽬53.6 题⽬63.7 题⽬7 1.sizeof和strlen的对比 1.1 sizeof …

大模型,也在卷价格

“百模大战”已从算力战、规模战蔓延到了价格战。 5月15日&#xff0c;字节跳动宣布豆包主力模型&#xff08;小于等于32K&#xff09;在企业市场的定价只有0.0008元/千Tokens&#xff0c;0.8厘就能处理1500多个汉字&#xff0c;比行业便宜99.3%&#xff1b;5月21日&#xff0…

算法分析与设计期末考试复习(更新ing)

重点内容&#xff1a; 绪论&#xff1a; 简单的递推方程求解 1.19(1)(2) 、 教材例题 多个函数按照阶的大小排序 1.18 分治法&#xff1a; 分治法解决芯片测试问题 计算a^n的复杂度为logn的算法&#xff08;快速幂&#xff09; 分治法解决平面最近点对问…

hot100 -- 二分查找

目录 前言 &#x1f382;搜索插入位置 &#x1f33c;搜索二维矩阵 &#x1f33c;排序数组元素第一和最后一个位置 &#x1f33c;旋转排序数组 &#x1f4aa;旋转排序数组中的最小值 &#x1f4aa;两个正序数组的中位数 前言 二分算法学习_时间超限ac:0%-CSDN博客 &#…

【个人博客搭建】(22)申请QQ开发者

这里我们要引入的一个概念是OAuth - OAuth 2.0是一个行业标准的授权协议&#xff0c;用于处理用户数据访问和分享的安全问题。它允许用户将他们对某些服务的访问权限授权给第三方应用&#xff0c;而无需分享他们的用户名和密码。以下是对OAuth 2.0的介绍&#xff1a; 基本概念 …

Flutter中同步与异步

一&#xff0c;同步/异步的理解 1&#xff0c;await&#xff1a;同步机制 同步操作会阻止其他操作执行&#xff0c;直到完成为止。同步就好比打电话一样&#xff0c;打电话时都是一个人在说另一个人听&#xff0c;一个人在说的时候另一个人等待&#xff0c;等另一个人说完后再…

Python001

Python 是一种高级编程语言。它具有以下显著特点&#xff1a;1. 简单易学&#xff1a;语法相对简洁明了&#xff0c;对初学者很友好。2. 丰富的库&#xff1a;拥有大量强大的内置库和第三方库&#xff0c;可用于各种领域&#xff0c;如数据分析、机器学习、Web 开发等。3. 可读…

基于STM32开发的智能语音控制系统

目录 引言环境准备智能语音控制系统基础代码实现&#xff1a;实现智能语音控制系统 4.1 语音识别模块数据读取4.2 设备控制4.3 实时数据监控与处理4.4 用户界面与反馈显示应用场景&#xff1a;语音控制的家居设备管理问题解决方案与优化收尾与总结 1. 引言 随着人工智能技术…

C51学习归纳7 --- LED点阵显示静态图片和动画

今天学习一个非常常用的功能。外面的流动字母的LED大屏大家应该很常见吧。今天&#xff01;学完这个&#xff0c;你就可以自己设计一个LED大屏了&#xff01; 一、开发板原理图 首先我们看点阵屏幕的输入信号&#xff0c;有P0_X和DP_X控制。P0_X直接就是芯片的P0输出端口&…

vb开源项目推荐:PhotoDemon9.0一键批量去除图片水印

PhotoDemon 9.0作为一款开源免费的照片编辑器&#xff0c;提供了丰富的图片编辑和处理功能&#xff0c;可以通过PhotoDemon的批处理功能结合一些编辑技巧&#xff0c;来实现批量去除图片水印的目的。 以下是一个可能的步骤指南&#xff0c;用于在PhotoDemon 9.0中通过批处理间…

无人机EasyDSS推拉流视频直播技术在农业植保中的精准应用与展望

随着科技的飞速发展&#xff0c;无人机在农业领域的应用越来越广泛&#xff0c;特别是在农业植保方面&#xff0c;无人机以其独特的优势&#xff0c;为农业生产带来了革命性的改变。 无人机在农业植保中的应用主要体现在两个方面&#xff1a;提高工作效率和精准喷洒药物。在以…

SM201,SM203主控模块备件

SM201,SM203主控模块备件。MACSV软件安装&#xff1b;二、软件组成及各部分功能&#xff1b;三、组态流程&#xff1b;四、组态详解SM201,SM203主控模块备件&#xff08;组态各部分的操作过程及基本原理&#xff09;。一、MACSV系统软件安装软件安装——计算机角色在每台计算机…

Unity 之 代码修改材质球贴图

Unity 之 代码修改材质球贴图 代码修改Shader&#xff1a;ShaderGraph&#xff1a;材质球包含属性 代码修改 meshRenderer.material.SetTexture("_Emission", texture);Shader&#xff1a; ShaderGraph&#xff1a; 材质球包含属性 materials[k].HasProperty("…

LlamaIndex三 配置

前言 在上篇LlamIndex二 RAG应用开发 - 掘金 (juejin.cn)中&#xff0c;我们学习到LlamaIndex对RAG的全面支持。这篇文章&#xff0c;我们就来细化这个过程&#xff0c;尝试各种配置选项&#xff0c;满足不同场景需求。学习过后&#xff0c;大家再开发RAG应用&#xff0c;会更…

Vue11-键盘事件

一、键盘事件&#xff1a;keydown和keyup事件 keydown 和 keyup 是两种常用于处理键盘输入事件的JavaScript事件。当你在网页的输入框或其他可输入元素上按下或释放键盘上的某个键时&#xff0c;这些事件就会被触发。 1-1、keydown 事件 当用户按下键盘上的某个键时&#xff…

matplotlib 动态显示梯度下降过程

文章目录 简介曲线下降曲面下降 简介 梯度下降是一种优化算法&#xff0c;常用于寻找函数的最小值或最大值。它通过迭代更新参数的方式逐步减小&#xff08;或增大&#xff09;目标函数的值&#xff0c;直到达到某个停止条件为止。梯度下降的基本思想是沿着目标函数的负梯度方…