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  // 默认值为0static 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 inColor.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 inColor.clear.preference(key: WidthPreferenceKey.self, value: geometry.size.width)})}.onPreferenceChange(WidthPreferenceKey.self) { width inif let width = width {print("Received width: \(width)")}}}

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

计算ScrollView的偏移量offsetY

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

struct PreferenceDemo: View {@Namespace var scrollViewSpacevar body: some View {ScrollView(.vertical, showsIndicators: false) {VStack {ForEach(0..<100) { index inText("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 inprint("offSetY: \(value)")}}.coordinateSpace(name: scrollViewSpace)}
}private struct ScrollOffsetPreferenceKey: PreferenceKey {static var defaultValue = CGFloat.zerostatic func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {value += nextValue()}
}

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

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

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

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

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

写在最后

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

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Eslint配置指南

1. Eslint配置指南 1.1. 安装 ESLint1.2. 生成配置文件1.3. 修改配置文件1.4. 创建 .eslintignore 文件1.5. 运行 ESLint1.6. 整合到编辑器/IDE1.7. 自动修复 2. 配置prettier 2.1. 安装依赖包2.2. .prettierrc.json添加规则2.3. .prettierignore忽略文件2.4. 保存自动格式化 3…

实战 | 通过微调SegFormer改进车道检测效果(数据集 + 源码)

背景介绍 SegFormer&#xff1a;实例分割在自动驾驶汽车技术的快速发展中发挥了关键作用。对于任何在道路上行驶的车辆来说&#xff0c;车道检测都是必不可少的。车道是道路上的标记&#xff0c;有助于区分道路上可行驶区域和不可行驶区域。车道检测算法有很多种&#xff0c;每…

vue2实现将el-table表格数据导出为长图片

方法一、 el-table数据导出为长图片 将el-table数据导出为图片不是一个直接的功能&#xff0c;但可以通过以下步骤实现&#xff1a; 使用html2canvas库将表格区域转换为画布(canvas)。 使用canvas的toDataURL方法将画布导出为图片格式&#xff08;例如PNG&#xff09;。 创建…

数据结构--实验

话不多说&#xff0c;直接启动&#xff01;&#x1f44c;&#x1f923; 目录 一、线性表&#x1f60e; 1、建立链表 2、插入元素 3、删除特定位置的元素 4、输出特定元素值的位置 5、输出特定位置的元素值 6、输出整个链表 实现 二、栈和队列&#x1f618; 栈 顺序栈 …

将web项目打包成electron桌面端教程(一)vue3+vite+js

说明&#xff1a;后续项目需要web端和桌面端&#xff0c;为了提高开发效率&#xff0c;准备直接将web端的代码打包成桌面端&#xff0c;在此提前记录一下demo打包的过程&#xff0c;需要注意的是vue2或者vue3的打包方式各不同&#xff0c;如果你的项目不是vue3vitejs&#xff0…

数字孪生技术体系和核心能力整理

最近对数字孪生技术进行了跟踪调研学习,整理形成了调研成果,供大家参考。通过学习,发现数字孪生技术的构建过程其实就是数字孪生体的构建与应用过程,数字孪生体的构建是一个体系化的系统工程,数字化转型的最终形态应该就是数实融合互动互联的终极状态。数实融合是每个行业…

[每周一更]-(第100期):介绍 goctl自动生成代码

​ 在自己组件库中&#xff0c;由于部分设计会存在重复引用各个模板的文件&#xff0c;并且基础架构中需要基础模块内容&#xff0c;就想到自动生成代码模板&#xff0c;刚好之前有使用过goctl&#xff0c;以下就简单描述下gozero中goctl场景和逻辑&#xff0c;后续自己借鉴将自…

英语学习笔记32——What‘s he/she/it doing?

What’s he/she/it doing? 他/她/它 正在做什么&#xff1f; 词汇 Vocabulary type /taɪp/ v. 打字 n. 类型&#xff0c;签字 ing形式&#xff1a;typeing 用法&#xff1a;this type of …    这种类型的…… 例句&#xff1a;我喜欢这种苹果。    I like this type…

java自学阶段二:JavaWeb开发--day80(项目实战2之苍穹外卖)

《项目案例—黑马苍穹外卖》 目录&#xff1a; 学习目标项目介绍前端环境搭建(前期直接导入老师的项目&#xff0c;后期自己敲&#xff09;后端环境搭建&#xff08;导入初始项目&#xff0c;新建仓库使用git管理项目&#xff0c;新建数据库&#xff0c;修改登录功能&#xff…

[office] 如何在Excel中拉动单元格时表头不变形- #学习方法#职场发展#经验分享

如何在Excel中拉动单元格时表头不变形? 如何在Excel中拉动单元格时表头不变形&#xff1f;Excel是我们常用的办公软件&#xff0c;当我们使用Excel拉动单元格时表头不变形&#xff0c;该如何操作呢&#xff0c;下面小编就为大家做详细讲解 如何在Excel中拉动单元格时表头不变…

用户输入表格数据设计(XPTable控件使用说明九)

XP Table控件可以编辑数据&#xff0c;程序也可以使用编辑后的数据&#xff0c;但是程序新建时又从初始化数据到模型到显示&#xff0c;这两步有点绕&#xff0c;做了一个实例来说明这块内容。 流程1&#xff1a;初始化数据--> model--> UI show 流程2&#xff1a;UI--…

skywalking基础使用

skywalking基础使用 找链路追踪Id将链路追踪Id拿到skywalking-ui中筛选对应链路补充说明例如, sql的打印能让我们了解到代码中对应的sql是否符合预期 找链路追踪Id 在接口响应header中复制x-trace-id 这个接口响应正常了, 异常没有暴露到前端, 且调用链路很长, 但我们借助s…

【讲解下ECMAScript和JavaScript之间有何区别?】

&#x1f308;个人主页: 程序员不想敲代码啊 &#x1f3c6;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f44d;点赞⭐评论⭐收藏 &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff0c;让我们共…

第1章 起步

第1章 起步 1.1搭建编程环境1.2 在不同操作系统中搭建 Python 编程环境1.3 运行Hello world 程序1.4 解决安装问题1.5 从终端运行 Python 程序 1.1搭建编程环境 1.1.1 Python 版本 本书编写期间的最新版本为 Python 3.7 1.1.2 运行 Python 代码片段 Python 自带一个在终端窗口…

树莓派4B 零起点(一) 树莓派 无屏 从购买到启动

目录 背景 一. 准备工作 二、烧录系统 三、连接系统 背景 准备开发ROS机器人&#xff0c;在淘宝上购买的树莓派4B(4G)到货了&#xff0c;配件都很齐全&#xff0c;那么就直接开箱验货。 一. 准备工作 1 、硬件&#xff1a;(如下图) (我的购买链接: 树莓派4B 4g 套件) 2…

Java Web学习笔记26——Element常用组件

常见组件&#xff1a; 就是一个复制和粘贴的过程。 Table表格&#xff1a;用于展示多条结构类的数据&#xff0c;可对数据进行排序、筛选、对比或其他自定义操作。 常见组件-分页主键&#xff1a; Pagination&#xff1a;分页&#xff1a;当数据量比较多时&#xff0c;使用分…

【WEB前端2024】智体OS:poplang编程控制成本小千元的长续航robot机器人底盘(开源)

【WEB前端2024】智体OS&#xff1a;poplang编程控制成本小千元的长续航robot机器人底盘&#xff08;开源&#xff09; 前言&#xff1a;dtns.network是一款主要由JavaScript编写的智体世界引擎&#xff08;内嵌了three.js编辑器的定制版-支持以第一视角游览3D场馆&#xff09;…

网页文档下载不了怎么办 网页文档下载方法

一个方法&#xff0c;搞定所有网页文档下载。如果你也需要从网页下载各种文档&#xff0c;那么本文一定可以帮到你。无须充值会员&#xff0c;各大平台文档下到爽。看到就是赚到&#xff0c;还不赶快学起来。有关网页文档下载不了怎么办&#xff0c;网页文档下载方法的问题&…

端午与高考的交汇点:家的温暖与梦想的起点

当端午节的粽香弥漫在街头巷尾&#xff0c;高考的脚步也悄然而至。这两个看似毫无关联的时刻&#xff0c;却在每年的六月&#xff0c;奇妙地交汇在一起&#xff0c;为我们带来了一段特别的记忆。这不仅是家的温暖与梦想的起点相遇的时刻&#xff0c;更是传统文化与现代追求共融…

Redis进阶知识个人汇总

持久化 三种方式实现它的持久化&#xff1a; RDB持久化 全称Redis数据备份文件&#xff0c;又称Redis数据快照 这种就是将Redis内存中所有数据记录到磁盘中&#xff0c;当实例出故障后&#xff0c;从磁盘中读快照文件进行恢复数据。 一般使用bgsave指令实现 复制主线程得到一…