SwiftUI 6.0(Xcode 16)新 PreviewModifier 协议让预览调试如虎添翼

在这里插入图片描述

概览

用 SwiftUI 框架开发过应用的小伙伴们都知道,SwiftUI 中的视图由各种属性和绑定“扑朔迷离”的缠绕在一起,自成体系。

在这里插入图片描述

想要在 Xcode 预览中泰然处之的调试 SwiftUI 视图有时并不是件容易的事。其中,最让人秃头码农们头疼的恐怕就要数如何正确的向预览传入视图内部的状态了。

在本篇博文中,您将学到如下内容:

  • 概览
  • 1. PreviewModifier 到底有啥用?
  • 2. 用 PreviewModifier “点缀”预览视图外观
  • 3. PreviewModifier 诞生之前我们如何向预览传送数据?
  • 4. 用 PreviewModifier 注入(inject)预览模拟数据
  • 总结

遵循 SwiftUI 6.0(Xcode 16)新推出的 PreviewModifier 协议,正可谓是:“你好,我好,大家都好”。

不信?且看分晓!Let‘s go!!!😉


1. PreviewModifier 到底有啥用?

从 SwiftUI 6.0 开始,顺便借助 Xcode 16 的东风,苹果推出了全新的 PreviewModifier 协议:

在这里插入图片描述

正如该协议“自夸”的那样:PreviewModifier 可以让 Xcode 预览(Preview)界面和调试数据“浑然天成,融洽无间”。

有了 PreviewModifier,我们即可从两个方面来为预览调试“雪中送炭”:

  • 统一改变预览中视图的外观;
  • 为预览视图传入模拟测试数据;

下面,就让我们依次来看看它们究竟是如何“大施拳脚”的吧。

2. 用 PreviewModifier “点缀”预览视图外观

假若我们希望 Xcode 预览中某些被调试的视图都放在导航容器中,并且根据实际情况增加导航标题和导航栏 Logo。

注意,这些视图的 body 代码自身并没有嵌入到导航视图内,因为这是使用它们的父视图份内的事儿。这意味着,我们必须繁文缛节的在所有预览中将这些视图嵌入到导航视图中去:

#Preview {NavigationStack {ContentView().navigationTitle("SwiftUI 滚动行为演示").toolbar {Text("大熊猫侯佩 @ \(Text("CSDN").foregroundStyle(.red))").foregroundStyle(.orange).font(.headline.weight(.heavy))}}
}

如上代码所示:我们不但要在预览中为每个“潜在的”导航子视图添加导航容器(NavigationStack),还要不厌其烦的为它们设置相应的导航标题和 Logo 视图。

现在,我们看看 PreviewModifier 协议能为我们做些什么吧:

struct NavPreviewHelper: PreviewModifier {var title: Stringfunc body(content: Content, context: Void) -> some View {NavigationStack {content.navigationTitle(title).toolbar {Text("大熊猫侯佩 @ \(Text("CSDN").foregroundStyle(.red))").foregroundStyle(.orange).font(.headline.weight(.heavy))}}}
}

如大家所见,我们创建了一个 NavPreviewHelper 结构,并让其遵循 PreviewModifier 协议。我们只需实现其中的 body 方法,然后将预览测试的视图包裹在导航容器(NavigationStack)内部,并妥善为其设置了标题和预览 Logo。

有了 NavPreviewHelper 这位“贤内助”,我们可以易如反掌的将任何需要“如此炮制”的预览测试视图嵌入到 NavigationStack 中并妥善“装扮”了:

#Preview(traits: .modifier(NavPreviewHelper(title: "SwiftUI 滚动行为演示"))) {ContentView()
}#Preview("另一个视图", traits: .modifier(NavPreviewHelper(title: "另一个视图演示"))) {Text("另一个测试视图!")
}

从下面的演示中大家可以看到,我们的 NavPreviewHelper 就像“预览模版”一样,可供任何有此需求的预览视图使用了:

在这里插入图片描述

我们甚至还能通过 PreviewTrait 扩展,进一步简化 #Preview 宏中 NavPreviewHelper 的调用:

extension PreviewTrait where T == Preview.ViewTraits {@MainActor static func navHelper(_ title: String) -> PreviewTrait<T> {.modifier(NavPreviewHelper(title: title))}
}#Preview(traits: .navHelper("SwiftUI 滚动行为演示")) {ContentView()
}#Preview("另一个视图", traits: .navHelper("另一个视图演示")) {Text("另一个测试视图!")
}

3. PreviewModifier 诞生之前我们如何向预览传送数据?

在 PreviewModifier 降临之前,倘若我们想要为视图传入预览测试数据,在某些场景下非得大费周章一番不可:

#Preview {@Previewable var clock = Clock.newCountClock(name: "大熊猫侯佩的计时器")let container = ModelContainer.previewtry! container.mainContext.save(clock)return CountClockCell(clockID: clock.id).modelContainer(container)
}

如上代码所示:我们需要为所有使用 Clock 托管实例的预览视图“喋喋不休”的初始化测试数据。虽然使用 SwiftUI 6.0 新加入的 @Previewable 宏能够让实现简洁不少,但在每个预览视图之前都来上这么“一坨”代码,恐怕也绝非长久之计。毕竟,它严重违反了 DRYKISS原则。


关于 SwiftUI 6.0(Xcode 16) 中预览新增 @Previewable 宏的使用,请小伙伴们移步如下链接观赏更详细的内容:

  • SwiftUI 6.0(Xcode 16)全新 @Entry 和 @Previewable 宏让开发妙趣横生

4. 用 PreviewModifier 注入(inject)预览模拟数据

现在,我们回到拥有 PreviewModifier 色彩斑斓的“新世界”中吧。

回忆一下之前 NavPreviewHelper 类型的实现:我们实际上完全忽略了 body 方法中的 context 参数(所以将其类型设置为 Void)。其实,这个 context 可以大有作为。

为了让 context 物尽其用,我们需要另外实现一个 makeSharedContext 方法,用它来注入测试模型数据。

现在,我们尝试将前一个需要 Clock 模型数据的预览改写为 PreviewModifier “助人为乐”的形式:

var clockID: UUID?
private struct MockClockData: PreviewModifier {static func makeSharedContext() throws -> ModelContainer {let container = ModelContainer.previewlet clock = Clock.newCountClock(name: "大熊猫的计时器")clockID = clock.idtry container.mainContext.save(clock)return container}func body(content: Content, context: ModelContainer) -> some View {content.modelContainer(context)}
}extension PreviewTrait where T == Preview.ViewTraits {@available(watchOS 11.0, *)@MainActor static var sampleData: Self = .modifier(MockClockData())
}@available(watchOS 11.0, *)
#Preview(traits: .mockClockData) {CountClockCell(clockID: clockID!)
}

从上面的代码可以看到,我们利用 makeSharedContext 方法在指定的 ModelContainer 中生成并保存了所需的测试数据,接下来在 Xcoce 预览中使用这些数据就是水到渠成的事情啦:

在这里插入图片描述

有了这位预览“好帮手”之后,我们现在可以为任何需要 Clock 托管数据的预览视图“套用” MockClockData “测试模版”啦,小伙伴们是不是觉得事倍功半,一步到位了呢?棒棒哒!💯

总结

在本篇博文中,我们介绍了如何使用 SwiftUI 6.0(Xcode 16)中最新的 PreviewModifier 协议让预览调试闲情逸致、如虎添翼。

感谢观赏,再会啦!😎

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

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

httpx,一个网络请求的 Python 新宠儿

大家好&#xff01;我是爱摸鱼的小鸿&#xff0c;关注我&#xff0c;收看每期的编程干货。 一个简单的库&#xff0c;也许能够开启我们的智慧之门&#xff0c; 一个普通的方法&#xff0c;也许能在危急时刻挽救我们于水深火热&#xff0c; 一个新颖的思维方式&#xff0c;也许能…

SpringBoot框架学习笔记(三):Lombok 和 Spring Initailizr

1 Lombok 1.1 Lombok 介绍 &#xff08;1&#xff09;Lombok 作用 简化JavaBean开发&#xff0c;可以使用Lombok的注解让代码更加简洁Java项目中&#xff0c;很多没有技术含量又必须存在的代码&#xff1a;POJO的getter/setter/toString&#xff1b;异常处理&#xff1b;I/O…

【Python实战因果推断】41_合成控制1

目录 Online Marketing Dataset 在之前了解了面板数据在因果识别方面的优势。也就是说&#xff0c;你不仅可以比较单位之间的关系&#xff0c;还可以比较单位的前世今生&#xff0c;这样你就可以用更可信的假设来估计反事实 。您还了解了差分法&#xff08;DID&#xff09;及其…

python调用MATLAB出错matlab.engine.MatlabExecutionError无法调用MATLAB函数报错

python调用MATLAB出错matlab.engine.MatlabExecutionError无法调用MATLAB函数报错 说明(废话)解决方案MATLAB异常乱码python矩阵转MATLAB矩阵matlab.engine.MatlabExecutionError 说明(废话) python调用MATLAB&#xff0c;调用m文件中的函数&#xff0c;刚开始都没有问题&…

玄机-第一章 应急响应- Linux入侵排查

文章目录 前言简介应急开始准备工作步骤 1步骤 2步骤 3步骤 4步骤5 总结 前言 作者这一次也是差一点一次过&#xff0c;因为没有经验的原因&#xff0c;或者说题目对问题描述不太对&#xff0c;如果说是求黑客反连的ip的话我或许就知道要执行一下留下来的那个 .elf 可疑文件。 …

SpringCloud之Nacos集群,让Nacos不再是谜

Nacos集群搭建 集群结构 Nacos的集群环境我们采用这种结构&#xff1a;3个Nacos注册中心1个MySql Nacos集群 我们在windows上安装3个Nacos节点。分配配置相关信息 application.properties: 持久化数据到mysql中 修改 cluster.conf.example为cluster.conf然后在里面写上相关…

ARM体系结构和接口技术(六)KEY按键实验① 按键轮询检测

文章目录 一、按键轮询&#xff08;一&#xff09;分析按键的电路连接1. 按键原理图2. 按键消抖 二、分析芯片手册&#xff08;一&#xff09; GPIO章节&#xff08;二&#xff09;RCC章节 三、代码&#xff08;一&#xff09;key.c&#xff08;二&#xff09;key.h 一、按键轮…

lua 游戏架构 之 LoaderWallet 异步加载

定义了一个名为LoaderWallet class&#xff0c;用于管理资源加载器&#xff08;Loader&#xff09;。这个类封装了资源加载的功能&#xff0c;包括异步加载&#xff0c;以及资源的释放和状态查询。下面是对代码的详细解释&#xff1a; ### 类定义和初始化 这里定义了一个名为…

QT CNA上位机报错 解决方案

QT编译报错: -lControlCAN 解决方案 更换三个文件&#xff0c;即可解决(QT 自带的是32位库&#xff0c;应使用64位库文件)

【Vue】快速入门:构建你的第一个Vue 3应用

文章目录 一、Vue简介二、环境搭建1. 安装Node.js和npm2. 安装Vue CLI 三、创建Vue项目四、项目结构介绍五、组件基础创建一个组件使用组件 六、模板语法插值指令v-bindv-ifv-for 七、事件处理八、状态管理安装Vuex创建Store使用Store 九、路由基础安装Vue Router配置路由使用路…

PiT : 基于池化层Pooling layer的Vision Transformer

CNN的降维原理;随着深度的增加,传统CNN的通道维数增加,空间维数减少。经验表明,这样的空间降维对变压器结构也是有益的,并在原有的ViT模型的基础上提出了一种新的基于池的视觉变压器(PiT)。 1. 引言 ViT与卷积神经网络(CNN)有很大的不同。将输入图像分成1616小块馈送到变压…

力扣刷题之2959.关闭分部的可行集合数目

题干描述 一个公司在全国有 n 个分部&#xff0c;它们之间有的有道路连接。一开始&#xff0c;所有分部通过这些道路两两之间互相可以到达。 公司意识到在分部之间旅行花费了太多时间&#xff0c;所以它们决定关闭一些分部&#xff08;也可能不关闭任何分部&#xff09;&…

mysql group_concat()函数、行转列函数

文章目录 一、group_concat函数1.1、语法1.2、示例1.2.1、查询所有姓名&#xff0c;并显示在一行1.2.2、单列合并&#xff0c;指定冒号分隔符1.2.3、单列合并&#xff0c;去重1.2.4、多列拼接合并1.2.5、多列拼接合并&#xff0c;列和列之间指定分隔符 在mysql的关联查询或子查…

周报0708-0715(run代码)

本周围绕代码展开学习&#xff0c;学习了组内的FWI代码&#xff0c;主要收获是熟练了创建环境、匹配解释器、安装必要包的流程&#xff0c;以及搜索时的小Tips&#xff1a;比如需要的包whl&#xff08;表示该包的编译版本&#xff09;。遇到的问题仍然不少 三个主要问题&#…

「实战应用」如何用图表控件LightningChart JS创建树状图应用?

LightningChart JS是Web上性能特高的图表库&#xff0c;具有出色的执行性能 - 使用高数据速率同时监控数十个数据源。 GPU加速和WebGL渲染确保您的设备的图形处理器得到有效利用&#xff0c;从而实现高刷新率和流畅的动画&#xff0c;常用于贸易&#xff0c;工程&#xff0c;航…

Go语言并发编程-同步和锁

同步和锁 概述 同步是并发编程的基本要素之一&#xff0c;我们通过channel可以完成多个goroutine间数据和信号的同步。 除了channel外&#xff0c;我们还可以使用go的官方同步包sync&#xff0c;sync/atomic 完成一些基础的同步功能。主要包含同步数据、锁、原子操作等。 一…

P3-AI产品经理-九五小庞

AI产品的数据流向 美团外卖&#xff0c;实时只能调度 美团28分钟送达需求的分析 AI产品常用的算法 常用算法 常见的AI算法解析 自然语言生成NLG语音识别&#xff1a;科大讯飞&#xff0c;通义千问 虚拟现实机器学习平台 决策管理系统生物特征识别技术 RPA(机器人流程自动…

ICE-BA原理

文章目录 主要思想约束建立IMU&Local & globalBAIMU预积分LocalBAGlobalBA求解方法常用的求解思路改进增量BA方法 论文&#xff1a; 《ICE-BA: Incremental, Consistent and Efficient Bundle Adjustment for Visual-Inertial SLAM》 ICE-BA&#xff1a;增量、一致且高…

多商户SaaS版扫码点餐系统开源

基于前后端分离的 多商户 SaaS 版扫码点餐系统&#xff0c;支持后台点餐、多人同时在线点餐、购物车共享、餐桌状态实时监控&#xff0c;菜品管理、餐桌管理等众多功能。 源码下载&#xff1a;多商户 SaaS 版扫码点餐系统.zip 功能特色 手机扫码点餐功能&#xff1a;用户可…

如何评价2023年国际高校数学建模竞赛B题?

问题1&#xff1a;在三星堆发现的一个外圆直径为85厘米&#xff0c;内圆直径为40厘米&#xff08;假设&#xff09;的青铜车轮&#xff0c;青铜车轮有5条车轴&#xff08;射线&#xff09;&#xff0c;5条内弧线本质是双曲线&#xff0c;顶点与内圆相切&#xff0c;内弧双曲线的…