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,一经查实,立即删除!

相关文章

Git 和 Github 的使用

补充内容&#xff1a;EasyHPC - Git入门教程【笔记】 文章目录 常用命令配置信息分支管理管理仓库 概念理解SSH 密钥HTTPS 和 SSH 的区别在本地生成 SSH key在 Github 上添加 SSH key 使用的例子同步本地仓库的修改到远程仓库拉取远程仓库的修改到本地仓库拉取远程仓库的分支并…

千益畅行:合法合规的旅游卡服务,真实可靠的旅游体验

近期&#xff0c;关于千益畅行旅游卡服务的讨论引起了广泛关注。然而&#xff0c;网络上出现了一些对其误解和质疑的声音。为了澄清事实&#xff0c;我们深入了解了千益畅行的运营模式和业务特点&#xff0c;发现它是一家合法合规的旅游卡服务提供商&#xff0c;为消费者提供真…

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…

深度学习中2D分割

深度学习中的2D图像分割 2D图像分割是深度学习中的一个重要任务&#xff0c;旨在将图像划分为不同的区域&#xff0c;每个区域对应于图像中的不同对象或背景。该任务广泛应用于医学影像分析、自动驾驶、卫星图像分析等领域。以下是对深度学习中2D图像分割的详细介绍&#xff0…

实战 | 通过微调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; 栈 顺序栈 …

Mybatis缓存的生命周期、使用的特殊情况

以下场景均在Spring Boot程序中&#xff0c;并非手动创建SqlSession使用。 在回答这个问题之前&#xff0c;我们先来回顾一下&#xff0c;Mybatis的一级二级缓存是啥。 一级二级缓存 是什么 一级缓存&#xff08;本地缓存&#xff09;&#xff1a;一级缓存是SqlSession级别的…

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

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

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

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

MSA算法满足条件后续之Blum定理

文章目录 引言Blum定理关于MSA算法的讨论 此文章属于文献研读内容&#xff0c;文章内容来源于以下文献 Warren B. Powell, Yosef Sheffi , (1982) The Convergence of Equilibrium Algorithms with Predetermined Step Sizes. Transportation Science ,16(1):45-55. http://dx.…

赶紧收藏!2024 年最常见 20道分布式、微服务面试题(三)

上一篇地址&#xff1a;赶紧收藏&#xff01;2024 年最常见 20道分布式、微服务面试题&#xff08;二&#xff09;-CSDN博客 五、微服务架构有哪些优点和缺点&#xff1f; 微服务架构是一种设计方法&#xff0c;它将应用程序分解为一组小型、独立、松散耦合的服务。每个服务都…

[每周一更]-(第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…

btstack协议栈---总目录

章节文章标题文章链接1.基础篇1.BLE协议栈全解一篇就够点击我2.实战篇1.Hello World example点击我2.

面试专区|【53道Java基础高频题整理(附答案背诵版)】

Java为什么被称为平台无关性语言&#xff1f; Java被称为平台无关性语言&#xff0c;是因为一旦Java代码被编译成字节码&#xff0c;这些字节码就可以在任何安装了Java虚拟机&#xff08;JVM&#xff09;的设备上运行&#xff0c;无论这个设备使用的是什么操作系统。这就是“一…

[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…