Xcode 15.0 新 #Preview 预览让 SwiftUI 界面调试更加悠然自得

在这里插入图片描述

概览

从 Xcode 15 开始,苹果推出了新的 #Preview 宏预览机制,它无论从语法还是灵活性上都远远超过之前的预览方式。#Preview 不但可以实时预览 SwiftUI 视图,而且对 UIKit 的界面预览也是信手拈来。

在这里插入图片描述

想学习新 #Preview 预览的一些超实用调试小妙招吗?那就“如意如意”随小伙伴们的心意吧!

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

  • 概览
  • 1. Xcode 15.0 新预览机制简介
  • 2. #Preview 让状态初始化如此轻松!
  • 3. 为什么 #Preview 中不能直接嵌入可变状态?
  • 4 #Preview + @Observable 宏构造可变 @Binding 实参
  • 总结

相信学完本课后,大家对于 Xcode 15+ 预览的使用以及 SwiftUI 界面调试会更加的轻车熟路!

那还等什么呢?让我们马上开始吧!Let‘s preview!!!😉


1. Xcode 15.0 新预览机制简介

从 Xcode 15 开始,苹果借助于 Swift 5.9 宏(Macro)的“东风”,也为我们带来了全新的 #Preview 预览机制。你猜的没错,它其实就是一个宏:

在这里插入图片描述

如上所示:我们将 #Preview 宏定义展开为了其原始代码的实现,大家可以清楚的看到 #Preview 宏背地里到底做了些神马。

在 Xcode 15 之前,小伙伴们需要使用遵循 PreviewProvider 协议的 Previews 结构来帮助我们预览指定的 SwiftUI 视图:

struct LaunchView_Previews: PreviewProvider {static var previews: some View {LaunchView().environmentObject(Model())}
}

而现在,只需一个 #Preview 即可搞定所有,岂不呜呼快哉:

#Preview {LaunchView().environmentObject(Model())
}

为了方便起见,我们还可以非常 nice 的将多个定制的 #Preview 预览内容混合在一起显示:

在这里插入图片描述

如上所示,为了便于观察我们在 #Preview 中指定了不同预览名称以及预览设备的方向和明暗主题等特性,简直小菜一碟。

2. #Preview 让状态初始化如此轻松!

“理想很骨感,现实却很残酷”。

在实际开发中,不可能所有视图都如此简单。在现实的 App 中视图多半都会与模型(数据)相绑定,这意味着我们在预览它们之前需要创建对应的数据,否则预览就不会达到预期效果,显示将是一片“空空如也”。

比如在 SwiftUI 里我们有一个分类选择视图(V2_ChallengeClassSelectView),所有内置(Built in)的分类都是从数据库中读取的,但前提是我们在数据库中已经初始化了这些分类,这是通过调用如下方法来完成的:

V2_ChallengeClassification.initializeData()

所以,我们可能会写出下面的代码以期待 #Preview 预览可以正常工作:

@available(iOS 17.0, *)
#Preview {V2_ChallengeClassSelectView(selecting: .constant(nil)).onAppear {try? V2_ChallengeClassification.initializeData()}
}

不过可惜的是,以上实现无法得偿所愿。原因是我们 V2_ChallengeClassSelectView 视图中的分类数据必须在其 body 显示之前就准备就绪:

@available(iOS 17.0, *)
struct V2_ChallengeClassSelectView: View {@Binding var selecting: V2_ChallengeClassification?let builtInClasses = try? V2_ChallengeClassification.allBuiltInClassifications()
}

对于这种情况,#Preview 宏有一个非常简单的解决方案:我们只需在预览内容之前直接调用初始化代码即可:

@available(iOS 17.0, *)
#Preview {try? V2_ChallengeClassification.initializeData()return V2_ChallengeClassSelectView(selecting: .constant(nil))
}

运行可以看到,我们已经能够在预览中正确显示初始化之后的所有内置分类了:

在这里插入图片描述

3. 为什么 #Preview 中不能直接嵌入可变状态?

大家可能已经发现了,上面示例中的 V2_ChallengeClassSelectView 视图包含一个 selecting 绑定状态:

struct V2_ChallengeClassSelectView: View {   @Binding var selecting: V2_ChallengeClassification?
}

但在我们的预览中,为了“偷懒”实际向其传入的是一个绑定常量:

V2_ChallengeClassSelectView(selecting: .constant(nil))

这样做的后果是:我们无法在预览中改变 selecting 属性的值,也就无法观察到视图中选择所产生的变化了。

小伙伴们可能会觉得,下面的实现可以帮我们摆脱这一问题:

@available(iOS 17.0, *)
#Preview {@State var selecting: V2_ChallengeClassification?try? V2_ChallengeClassification.initializeData()return V2_ChallengeClassSelectView(selecting: $selecting)}

遗憾的是这样做“然并卵”,毫无用处:

在这里插入图片描述

其原因是:与 Xcode 15 之前的旧预览机制类似,嵌入在预览结构中的简单状态实际上是无法被改变的,即使它被 @State 等(可变)限定符所修饰时也是如此。

那么我们如何解决呢?

答案很简单:将可变状态放到 #Preview 外面去!

4 #Preview + @Observable 宏构造可变 @Binding 实参

从 Xcode 15 (Swift 5.9)开始,苹果推出了新的 @Observable 宏帮我们便捷的创建可观察对象。


更多关于 @Observable 宏以及 Observation 框架的详细介绍,小伙伴们可以移步到下面的博文中进一步观赏:

  • Swift 5.9 与 SwiftUI 5.0 中新 Observation 框架应用之深入浅出
  • Swift 5.9 新 @Observable 对象在 SwiftUI 使用中的陷阱与解决

简单来说,我们可以在 #Preview 之外利用 @Observable 宏包裹我们的可变状态,从而可以将其通过绑定传入到对应的视图中去:

@available(iOS 17.0, *)
@Observable
class PreviewModel {var selecting: V2_ChallengeClassification?
}@available(iOS 17.0, *)
#Preview {@State var model = PreviewModel()try? V2_ChallengeClassification.initializeData()return V2_ChallengeClassSelectView(selecting: $model.selecting)
}

注意,在上面的代码示例中我们实际向 V2_ChallengeClassSelectView 视图传递的绑定是 @Observable 可观察对象 model 中的属性。虽然 model 作为 @State 放在了预览内部,不过由于它是一个可观察对象,所以它仍然可以变化自如。

编译运行修改后的代码,我们现在可以在 #Preview 预览界面中顺畅自如的测试 selecting 分类属性改变时的显示逻辑了:

在这里插入图片描述

至此,我们通过上面几个小“栗子”对 Xcode 15 中新的 #Preview 预览机制又有了更深刻的领悟,小伙伴们还不赶快给自己点一个大大的赞吧!👍🏻


想要系统学习 Swift 语言的小伙伴们,千万不要错过我的《Swift 语言开发精讲》专栏哦:

在这里插入图片描述

  • Swift 语言开发精讲

总结

在本篇博文中,我们介绍了 Xcode 15+ 中新的 #Preview 预览机制,并讨论了如何利用 #Preview + @Observable 宏让 SwiftUI 界面调试更加“如虎添翼”。

感谢观赏,再会啦!😎

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

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

相关文章

什么场景适合使用Traefik?

Traefik 作为一款现代的反向代理和负载均衡器,已经成为云原生环境中的热门选择。它提供的动态配置能力和强大的自动化功能使其在多种场景中非常有用。本文将详细探讨适合使用 Traefik 的几种关键场景,并解释为何在这些情况下它特别有用 🌍&am…

Java基础第十一课——类与对象(2)

由于类与对象这一部分的知识点很多,而且操作方法也有很多,所以这次将继续深入讨论一下关于类与对象中方法传参、方法重载、构造方法以及this关键字使用方面的知识。 一、方法传参 1.return关键字 return关键字作用 作用场景:方法内 作用…

AndroidAutomotive模块介绍(二)应用及接口介绍

前言 上一篇文章中从整体角度描述了 Android Automotive 模块。本篇文章将对 Android Automotive 中的 APP 以及 API 部分展开描述。 上一篇:AndroidAutomotive模块介绍(一)整体介绍 下一篇:AndroidAutomotive模块介绍&#xff0…

Sam Altman新动向!被曝公开撬金主微软的客户!

Sam Altman向大公司们推销ChatGPT企业版,这其中包括一些微软的客户! 好好好! 你小子怎么回事!金主的客户也不放过了是吧! 根据路透社4月12日的报道,OpenAI首席执行官Sam Altman本月在旧金山、纽约和伦敦举…

数据结构之单链表的相关知识点及应用

找往期文章包括但不限于本期文章中不懂的知识点: 个人主页:我要学编程(ಥ_ಥ)-CSDN博客 所属专栏:数据结构 目录 链表的概念及结构 链表与顺序表的区别与优劣势 链表的分类 单链表的实现 单链表中增加节点 单链表中尾插数据 打印单链…

新手做抖店需要掌握的技巧!这些运营方法,保证让你快速出单!

哈喽~我是电商月月 新手做抖店做不好?除了没努力的原因,还有一点,就是缺少运营的方法! 今天我就给大家普及几个运营过程中非常重要的技巧! 这都是我在运营中总结出来的能提高出单率的干货,新手建议收藏 …

常见的垃圾回收器(上)

文章目录 Serial垃圾回收器 SerialOld垃圾回收器PS POParNewCMS 常见的垃圾回收器(下) Serial垃圾回收器 SerialOld垃圾回收器 Serial是一种单线程串行回收年轻代的垃圾回收器 回收年代和算法 年轻代:复制算法 老年代:标记-整…

grpc-教程(golang版)

目录 一、介绍 二、环境准备 三、Golang中使用grpc 1.编写protobuf文件 2.服务端 3.客户端 四、proto文件详解 1.proto语法 2.数据类型 基本数据类型 数组类型 map类型 嵌套类型 编写风格 3.多服务 4.多个proto文件 五、流式传输 1.普通rpc 2.服务器流式 …

ACL 2024 commit是否提交revision版本的论文

ACL 2024 commit是否提交revision版本的论文? 有大佬知道吗?!! 哎 ARR rebuttal阶段 让我们加实验,回复一时爽。。 现在又要提交pdf到ACL会议了,是提交之前的ARR版本的稿子,还是我承诺的 revision 稿啊&…

Aritest+python+Jenkins解放双手iOS/Android自动化

ARITest、Python 和 Jenkins 可以结合在一起创建一个自动化测试解决方案,实现持续集成和持续测试的目标。以下是三者如何协同工作的基本概念: 1. **ARITest**: ARITest 是一款功能全面的自动化测试工具,提供 UI 自动化、接口自…

【python】基于pyttsx3库的字符串转音频文件

一、源码 import pyttsx3 engine pyttsx3.init() engine.setProperty(volume, 0.8) engine.setProperty(rate, 150) engine.save_to_file("Hello, World!", "output.mp3") engine.runAndWait()二、介绍 使用pyttsx3库,设置声音与速率&#x…

芯片低功耗VCLP

​VCLP(VC Low Power)是Synopsys提供的一款低功耗静态规则检查工具,它能够帮助验证和清洁IEEE 1801 Unified Power Format (UPF)低功耗设计意图,并确保UPF中的功耗意图与实现一致。VCLP通过执行语法和语义检查,有助于在…