大多数浏览器和
Developer App 均支持流媒体播放。
-
为 SwiftUI 预览组织你的 app
在开发过程中使用 SwiftUI 预览时,可以快速创建更灵活和可维护的 app。了解通过细微调整项目来改善预览体验。了解如何一次预览多个文件,如何管理预览的数据流以及如何在预览时使用样本数据。我们还将为你提供定义视图输入的策略,使其更加可预览和可测试。 为了充分利用本节内容,你需要对 SwiftUI 有所了解。有关在 Xcode 中与 SwiftUI 预览进行交互的介绍,请查看WWDC 20 中的“可视化编辑 SwiftUI 视图”。
资源
相关视频
WWDC20
-
下载
(你好 WWDC 2020) 你好 欢迎参加 WWDC (为 SwiftUI 预览组织你的 app) 嗨 我叫凯文 我从事 Xcode 预览的工作 Xcode 预览帮助你编写 SwiftUI 代码 同时以多个配置编辑视图 并快速测试所有更改 但预览在整体上使你的 app 受益 通过组织 app 使其更易于预览 可以使你的 app 更易于理解 更易于测试并更易于维护 在这段视频中 我想向你展示四种 组织 app 的方法 以便最充分地利用预览 首先 我们会看看如何同时预览多个文件 以便你能在更广阔的上下文中编辑视图
其次 我们会看看预览 和 SwiftUI app 生命周期之间的关系 以便你能在定义显式数据依赖关系的同时 获得出色的预览性能 第三 说到数据 我们会看看在哪里定义示例数据 以便你能在开发过程中有一整套例子 而不影响已部署 app 的大小 最后 我们来看一些 使你的视图更易于预览的技巧 在这段视频中我们有很多内容要讲 我们开始吧 在本视频中 我会用同一个 app 进行所有演示 这是一个为我拍摄的照片 创建拼贴画的 app 你可以创建拼贴画 选择布局 选一些照片 添加好友 而且 像所有优秀的照片 app 一样 甚至能添加一些效果 首先 让我们看看如何同时预览多个文件 (跨文件预览) 我们先从缩略图视图开始 以便为拼贴画选择布局 我这里有一个灰色的矩形 在这个白色背景下它看起来很棒 但它在上下文中看起来怎么样呢? 为此 我会切换到选择布局的选择器 现在我有两个预览 一个在白色背景上显示选择器 另一个在图像顶部显示它 这是为了显示 当用户捏拉缩放拼贴画时 内容滑到那个选择器下面时的样子 你可以看到 当后面有内容时 缩略图会消失 我想让那个颜色更突出一点 但在我编辑那个颜色时 我不想失去使用它的上下文
我可以通过点击底部栏中的该按钮 来锁定预览 以实现这一点 当我切换回布局缩略图视图时 Xcode 显示我刚刚锁定的视图的预览 以及正在处理的文件的预览 Xcode 还添加分隔符 以显示每组预览的来源 现在我可以编辑这个颜色了 我们把这个改成 70% 看起来好一点了 但 80% 看起来更好 但这个在深色模式下看起来怎么样? 我会通过点击“加号”按钮复制预览
我会选择预览 打开inspector 选一个深色外观…
现在当我看到这个预览时 那个灰色看起来有点不合适 我想为深色模式自定义这个灰色 建议的方法是使用资产目录下的资产
我要选择颜色 我已经设置了颜色 因此我要打开库 转到我的颜色 然后选择 grid 占位符 现在什么都没改变 因为我还没为深色模式自定义这个颜色 让我们执行该操作 但当我这样做时 我希望能编辑那个颜色 同样 在我在这个缩略图中 使用它的上下文中 我要从布局选择器取消对预览的锁定 并为布局缩略图重新锁定预览 让我们进入资产目录以编辑该颜色 在这里你可以看到那个灰色 我会选择它 并用 inspector 为深色模式添加自定义项
你可以选择那个自定义项 我会把这个改为 20%
现在我会保存资产目录
继续预览 Xcode 会重新构建 app 并向我展示这个新颜色 在上下文中是什么样子 这看起来很棒
我想向你展示另一个锁定的用例 通过在整个开发过程中 引入可能不需要的额外预览
使用导航器 我要前往 这个额外的预览文件中的另一组预览 这为我展示了 辅助功能布局的几个不同的预览 现在它们看起来都一样 因为我还没自定义视图 以响应不同的辅助功能数量 让我们取消对预览的锁定 并锁定这些不同的辅助功能预览 然后我可以切换回缩略图
现在我可以根据尺寸类别 缩放缩略图的尺寸和间距 我已经通过环境传递了尺寸类别 并为内容尺寸类别添加了扩展 以提供适合我的 app 的比例
我可以通过选择每个需要用它的地方 并乘以尺寸类别比例来使用这个比例 现在它在所有这些不同的尺寸类别 看起来都很棒 正如你所料 我可以继续编辑预览 并看到这些更改 在所有那些不同尺寸下是什么样子
这是我们可以利用预览锁定的三种方式 无论是否在更广阔的上下文中进行编辑 能够编辑非 Swift 文件 还是能够为特定任务 引入额外的预览 接下来 我们来看看预览 和 SwiftUI app 生命周期之间的关系 这是在 iOS 14 和 iPadOS 14 中引入的 我们所有的 app 在启动时 都会进行某种形式的初始化 可能是从磁盘加载文档 与 CloudKit 连接 或与各种设备通信 无论是什么 关键在于 这项工作的成本往往很高 预览的好处在于 我们可以在项目中编辑小视图和叶视图 并在 canvas 中直接跳转到它们 对于这些预览 我们希望避免进行那个昂贵的初始化工作
从定义数据的显式依赖关系的角度 考虑这一点很重要 这不在于是不是为预览进行操作 而在于确定你希望那个操作什么时候开始
SwiftUI app生命周期为我们提供了 定义这种依赖关系的工具 让我们来看看它的工作方式
以下是我的 app 类型的定义 作为一个属性 我定义了我的网络模型 因此每次创建我的 app 时 它就会创建我的模型 让我们看看在预览下运行时 我的 app 在做什么 为此 我要在预览中附加一个调试器 我可以通过按住“播放”按钮 并选择调试器预览来实现这一点
我可以将调试器附加到我的任何预览 这非常棒 因为我可以做两件事: 第一 如果我设置了任何断点 我可以点击这些断点 并调试为什么我的预览 没有显示预期的结果 或者在该演示中 我要给你举一个例子 说明使用调试 gauge 如何能帮你了解 你的 app 在做什么 当我打开调试gauge时 我可以看到我的 app 在启动时做了大量工作 包括在中央处理器上 而且网络在收集大量数据 这是因为我在初始化那个数据模型
对于我看到的预览 我不需要做那项工作 我要利用 SwiftUI 的 StateObject属性包装器
让我们暂停预览
让我们将 atStateObject 添加到我们的属性
这会告诉 SwiftUI 只在 app 首次创建时 初始化数据模型 这使 SwiftUI 能响应 数据模型中的任何更改 此外 对预览而言 用 StateObject 创建的模型没有初始化 这给了我们一个很好的机会 仅在需要运行 app 时才进行工作
现在 让我们继续预览
让我们再次开始调试
让我们看看中央处理器的现状 这看起来好多了 我们的中央处理器或网络 都没在做任何不必要的工作
这是一个使用 StateObject 仅在实际需要时初始化数据的例子 要进一步了解 StateObject 查看视频“SwiftUI 中的 app 要点” 如果我们没有作为 StateObjects 的一部分初始化数据模型 那我们如何将数据输入到预览? 我们会分两部分来探讨这个问题 首先 让我们看看 在哪里定义预览的示例数据
我们的拼贴画编辑器是一个照片 app 在开发过程中 我希望有很多例子 以确保我的效果和布局都能正常工作
我定义了资产目录 并为其添加了一些 可以在预览中引用的图像 但我不想在我的最终 app 中 发布所有这些图像 我可以为我的目标利用 Xcode 的 开发资产
让我们切换到项目编辑器 滚动到底部 你会看到一个名为开发资产的部分
开发资产是指向文件或文件夹的路径 Xcode 只能在 app 的开发版本中 包含这些路径
如果你要从 Xcode 模板 创建 SwiftUI app 则 app 会为你预配置开发资产路径 这个就是 但你也可以轻松地添加你自己的 正如我所做的一样 你也可以将其添加到其它目标 我会切换到 Mosaic Kit 框架 点击“加号”按钮 输入“预览内容” 然后找到仅包含在 app 开发版本中的 额外预览内容
开发资产之所以很有用是因为 它们不仅适用于诸如资产目录的文件 还适用于代码 让我们看看 刚刚添加的预览内容文件夹中有什么
通过使用导航器 我们可以向里看 看到两个 Swift 文件 这些 Swift 文件包含仅在开发 调试和测试 app 时所需的代码 我不希望在部署 app 时包含这些 那个演示主要是说明 在哪里定义预览资产、资源和代码 但如何将数据导入预览呢? 为此 我们要使视图本身可预览 这是组织 app 以使其更易于预览 看到回报的地方 (组织视图) 我想从一个宏大的构想开始 使我们的 app 独一无二的是用户体验 但在我们独特的用户体验背后是 独特的数据模型 我称之为富数据类型 它可能来自Core Data或CloudKit 或我们构建的自定义的东西 但最终 用户将用简单数据类型 与我们的 app 交互 这可能是文本框中的字符串 或切换开关中的Boolean 中间是我们所有的视图 这些将富数据类型转译为简单数据类型 问题是 我们应该在 视图层次结构的哪里进行转译 一般来说 我们越快将富类型 转译成较简单的数据类型 我们的 app 就越易于重用、测试和预览 最大的激励是我们希望能轻松地添加预览 以便能以所有配置测试 app 这样它们看起来就都很棒
我们会看四个例子 说明如何组织视图 以使其更易于预览 首先 我们会看一个 将不可变的简单数据类型 传递到无需更改值的视图的例子 其次 我们会看一个用 SwiftUI 绑定 将简单数据类型传递到 需要更改这些值 的视图(inspector)的例子 第三 我们会使用泛型 以便将数据的抽象传递到视图 这样我们就可以将其传递给其它视图 最后 我们会看一个 与其它例子略有不同的例子 并用环境将输入传递到视图 首先 让我们看一个 将不可变的简单数据类型传递到单元 以便为拼贴画添加协作者的例子
我们会从查看协作者背后的数据模型开始 它有一些简单类型 例如这个颜色和最后贡献日期 但大多数模型均由 CKUserIdentity 支持 现在 让我们去看看单元
我们的单元将协作者作为输入 作为观察对象 让我们创建预览
我会创建单元 然后需要创建协作者 当我要填写 CKUserIdentity 时 我知道需要在 CK 数据库上创建提取操作 但在之前的一个演示中 我们正是关闭了CloudKit的数据模型 对于这个视图 我不需要完整模型 让我们看看另一种方法 这提出了一个非常重要的理念 即查看正在构建的用户界面 然后查看模型 并确定需要通过模型 传递到视图所需的最小数据量
让我们看看 CollaboratorCell 的另一个例子
在这个单元中 我们将名称 图像和连接状态作为个体简单输入传递 为此创建预览非常容易 我们会创建一个 CollaboratorCell… 这会叫那个名字-比如说Jane Doe-
一个图像-目前我们只传递一个空图像- 以及状态 我们先从在线 Jane 开始 就这样 我有一个该单元的预览 最棒的是可以轻松地创建 这个单元的多个配置 我要把这个预览复制几次
其中一次 我会创建离线 Jane
我们会给她一个图像 打开资产目录 选一个图像
我们还要确保文本在变大时 能正确地调整大小 我们会给她一个很长的中名
用这些简单数据类型作为输入 创建多个配置就是这么容易 但有时我们可能会使数据类型过于简单 在我们希望系统 根据用户在 app 中使用的区域设置 来设置值的格式时 可能会发生这种情况 例如 日期 或在本例中 名称 我们要传递 PersonNameComponents 而不是为我们的名称传递字符串
现在我们可以告诉 SwiftUI 在适当的时间 设置该名称的文本格式 这就像定义格式化程序…
然后将其传递到我们的文本一样容易
最后 让我们来更新预览 以创建名称组件 而不是字符串
我会选择静态字符串的每个实例…
并将其替换为调用 以创建 PersonNameComponents 现在 我可以刷新预览 你可以看到 创建 PersonNameComponents 并且仍然提供所需的所有配置有多容易
现在我们还可以在那个例子中添加中名 比如“Really Long Middle”
就这样 所有的预览看起来都很棒 而且我可以看到所有这些不同的配置 如果可以的话 要让视图输入是 简单、不可变的数据类型 但有时查看器需要更改值 其次 让我们看一个用 SwiftUI 绑定 传递查看器可以更改的 简单数据类型的例子
在这个 inspector 中 我要为图像作为观察对象传递插槽数据 一个插槽代表拼贴画上的一张照片 让我们来看看那个 ImageSlot 数据类型
这有一些简单类型 但其核心是备用 ClockKit 记录 正如我们从前面的例子中看到的 要创建记录 我需要将那个完整的 ClockKit 数据模型全部引入 我想让创建预览变得很容易 我要运用与之前的演示中相同的理念 我要查看用户界面 选出用户界面真正需要编辑的部分
让我们切换到 inspector 的不同版本
在本例中 我们要为 SlotEffects 传递绑定 这是一个非常简单的类型 只是每个不同的滤镜都有一些浮点值 创建预览真的很容易
我要创建一个 inspector 并传递一些效果 你可以看到我用的是常量绑定 这意味着在该视图中 绑定不能更改 但这正是我们要测试的 让我们添加一个例子 在其中我们可以更改这个绑定的值 要在预览中做到这一点 我要引入一个中间视图 它会存储一些 可作为绑定传递到视图的状态
让我们定义视图…
然后让我们定义一个新的预览 来创建新的视图并传递一些效果 这些效果被作为状态存储 inspector 会将其作为绑定传递
现在 当我们继续预览时 我们可以实时观看这个预览…
我们可以在 canvas 中与它进行交互 我还想再添加用户界面的一部分 它是一个按钮 使我能替换照片 我可以将整个图像插槽数据类型 传递到这个视图 并在这里执行所有的替换逻辑 但实际上 我希望客户端决定怎么做 因此 我要传递一个闭包 当按钮被按下时会被调用
我要添加一个闭包作为输入 然后添加那个按钮
这只是一个调用那个 replacePhotoHandler 的简单按钮 现在让我们更新预览 以便为这个回调提供一个值
对于第一个 我会传递一个空的闭包 但对于第二个 我想确定按钮真的起作用 为此 我会让我的回调更改我们的效果 这样我就可以看到它起作用了 现在 当我们继续预览时…
在我们点击按钮时 你可以看到饱和度在降低
但预览的好处在于 我不仅可以在 canvas 上与之交互 而且可以通过使用设备端预览 在多个地方与之交互 我有一个深色模式下的 iPhone XR 我也想把同一个预览放在那个设备上 我会点击“设备”按钮 点击 iPhone XR…
Xcode 现在会将这个预览 镜像到一个设备上
就像以前一样 我可以在设备上 与其进行充分交互
在 Xcode 12、iOS 14 和 iPadOS 14 中 设备端预览有新的体验 当你进行更改时 它们会无缝地反映到你的设备上 你会注意到 在第一次用 Xcode 12 使用设备端预览时 一个名为 Xcode 预览的图标 出现在你的主屏幕上 轻点该图标 即使你的手机与 Xcode 断开连接 你也可以返回上次查看的预览 这使其非常适合进行一些更改 将其放在预览中 向你的同事展示 这是使用绑定的简要介绍 我们可以继续为输入使用简单数据类型 同时也使我们的视图能使其变异 但有些情况下 我们需要将更丰富的数据类型 从一个视图传递到另一个视图 第三 让我们看一个用泛型 将数据传递到拼贴画的主编辑器的例子 让我先切换一下 看看拼贴画的模型是什么样子
像我们现有的其它类型一样 它由这个 CloudKit CKShare 记录支持 现在 和我们看到的其它例子一样 我需要那个完整的数据模型 来创建其中一个拼贴画模型对象 并将其传递到预览 我们可以用上一个演示中所用的方法 并用绑定传递数据 我们试试看
我会切换到拼贴画的编辑器
在这里你可以看到它对名称、布局 (是简单类型)和插槽数据进行了绑定 让我们创建预览
现在我会提供一个使用常量绑定的名称 以及使用常量绑定的布局 但当我看到插槽数据时 我注意到插槽数据仍在使用 我们在上一个演示中使用的富数据类型 这会使我很难为每个预览传递数据 因此 即便我在使用绑定 我仍然无法传递所需的数据 让我们退一步 用在其它演示中使用的方法 考虑我们正在构建的用户界面 以及从模型创建该用户界面 所需的最小数据量 在这个例子中 我们要定义一个协议 以创建我们所需的抽象
让我们来看看这个抽象 它被称为拼贴画协议 这定义了拼贴画所需的一切 以便可被用户界面编辑 我有名称、布局和一些插槽数据 但这些插槽数据也是抽象 我们的拼贴画的每个插槽 都必须有 imagePublisher 它是发布服务器 因为数据可以从云异步提供 已经在磁盘上或处于错误状态 它也提供这些效果
现在我们有了这个拼贴画协议 我们可以定义非常容易创建的 模型的设计时版本
我已经在框架的预览内容中 做了这个
你可以看到这个设计时拼贴画 有名称、布局和一些插槽 而且在使用这个 DesignTimeSlot DesignTimeSlot 有发布服务器和一些效果 现在让我们看看编辑器的一个版本 它允许传递模型的这个设计时版本
我会切换到这个编辑器 这个拼贴画编辑器在两方面是通用的 首先 它在拼贴画类型方面是通用的 这符合我们刚才看到的拼贴画协议 它在 ImagePickerType方面 也是通用的 这使我能为设计时定义较简单的用户界面 以便为拼贴画挑选照片 而不必每次想测试它时 都转至完整的照片库
现在你会看到 为拼贴画编辑器创建预览有多容易
只需调用我们的拼贴画编辑器 这需要两个输入 首先 我们的拼贴画
创建一个设计时拼贴画 正如我们所见 这需要几个输入 它需要名称 我们称其为“我的拼贴画” 需要布局 我们会从twoByTwo开始 它需要一些插槽数据 现在 我们只传递一些空数据
它也需要闭包 以创建这个照片挑选器 我已经创建了 这个照片挑选器的设计时版本 它只是从我们之前看到的 预览资产目录提取图像
就这样 我的拼贴画编辑器有了预览 而且能完全运作 但它在使用那个较简单的设计时数据模型
而且 因为它是视图 我还可以添加 view-specific modifiers 让我们给它添加一点填充
现在我可以在 canvas 中 测试这个拼贴画编辑器 通过点击“播放”按钮…
我可以轻点两次插槽 选择照片 或选择不同的布局
此外 我可以传递 我们的设计时插槽数据的例子
我可以选择地址 把它想象成列中的行 然后我可以传递我们的 DesignTimeImageSlot 的一个例子 这只是从我们的资产目录提取一个图像 然后传递一些空效果
我可以看到它在第二列 或第二行是什么样子 或者是不同的图像 或者我甚至可以传递一些效果 我想看看在 50% 时饱和度是怎样的
这在 iPhone 上看起来很棒 但在 iPad 上呢? 和上个例子一样 我们也会把这个预览放在设备端 我可以选择设备端按钮 然后选择“我的 iPad” 这会让我看到这个拼贴画编辑器 在 iPad 的横屏模式下是什么样子 就像我的 canvas 中的版本一样 这是完全交互式的 我轻点两次 选一张照片 我甚至可以打开 inspector 并添加一些效果
我能够测试这个拼贴画编辑器的所有功能 而无需创建支持 CloudKit 的数据模型 只需要使用数据模型的设计时版本
第四 让我们看一个 与其它例子稍有不同的例子 如果可以的话 你应该通过定义视图的输入 来定义视图之间的显式数据依赖关系 但有时通过环境传递数据很方便 让我们通过为显示与云同步的状态的视图 添加一些预览 以查看预览和环境之间的关系
让我们切换到 CloudSyncView
CloudSyncStatusView 将 CloudSyncStatus 视为输入 视为 EnvironmentObject 在 SwiftUI 中 当你有 EnvironmentObject 时 你需要为层次结构中 层次较高的对象提供值 在为这些以 EnvironmentObjects 为输入的视图创建预览时 还需要为每个预览提供一个值 让我们看看它的工作原理
我会创建CloudSyncStatusView…
然后附加 EnvironmentObject modifier
我会创建 CloudSyncStatus 有一个展示云在线时 我的视图是什么样子的预览 但就像我们的其它例子一样 在使用环境时 添加多个配置非常容易 我会复制我的预览…
这一次 我会传递当我们的云离线时 会发生什么
这里有云离线时的图标 让我们再次复制它 让我们确保能正确显示 它最后一次同步的时间 我们创建 lastSync 让我们试试“几小时前”
最后 为了确保面面俱到
让我们看看云在线并刚刚同步的时候
这里有一个用环境 将输入传递到视图的例子 就像其它显式输入一样 当我们使用这些较简单的类型时 很容易创建预览的多个配置
在最后一个例子中 我想把我们在这段视频中看到的 所有东西综合在一起 让我们切换到 app 的根视图
这个根视图和我们的拼贴画编辑器一样 在两方面是通用的: 第一 CollageType 即那个 CollageProtocol 以及 ImagePickerType 我定义了一个中间视图 它具有某种状态 存储着我们之前制作的 设计时拼贴画
然后创建其中一个根视图 并将拼贴作为绑定传递 当按下“加号”按钮时还有一个动作回调 这使我能创建其中一个设计时拼贴画 因为创建设计时拼贴画非常容易 这个闭包很容易填充
最后 它将传递 DesignTimePhotoPicker
在我使用预览时 我会附加一个带 CloudSyncStatus 的 EnvironmentObject
现在 我的 canvas 中有一个 功能齐全的 app 版本 我点击“播放”按钮 我可以点击“加号”按钮 添加一个新的拼贴画 进去选一个不同的布局 或选一张照片
我可以返回 我可以通过轻扫删除它
我也可以实时更改这个视图 让我们锁定预览 我想把缩略图放大一点 为此 我要切换到拼贴画列表
转到显示拼贴画和标题缩略图的 拼贴画标签 现在我可以进入这个值 将其改成较大的值 好 或许这有点太大了 让我们回到 30 看起来很棒
最后 这在 iPhone 中看起来很棒 但在深色外观的 iPhone 或 iPad 上怎么样呢? 我们可以同时看到多个设备端预览 我要点击设备端预览按钮 选择两个设备 我的 iPhone 和 iPad
Xcode 正在将我的 app 的完整功能版本 放在所有这些设备上 在 canvas 中 我可以再次添加 并轻扫以删除 在 iPhone 上 我可以在这里进入竖屏模式 或选一个不同的布局 哦 这看起来好多了 返回 在我的 iPad 上 我可以选择一个拼贴画 选一张照片 打开效果…
在不运行我的 app 的情况下 测试所有这些功能 最棒的是当我进行编辑时 它同时反映在所有这些设备上 我会选 30 我会将其改为 90 现在我可以确保我能看到那个缩略图 我希望你能看到 在为数据定义抽象时 在使用多个预览和多个上下文 来测试你所有的配置 而无需运行 app 时 预览有多强大
这里有四个例子 来说明如何组织视图 使其更易于预览 我们在这段视频中看到了很多内容 首先 我们看了如何同时预览多个文件 以便你能在更广阔的上下文中编辑视图 其次 我们看了预览 和 SwiftUI app 生命周期之间的关系 以及使用 StateObject 在需要时才初始化数据 第三 我们看了在哪里定义示例数据 以及如何通过使用开发资产 将其排除在发布产品之外 最后 我们看了能使你的视图 更易于预览的四种方法
我希望给你一个宏大的构想
使你的 app 更易于预览的投资 使你的 app 在整体上受益 我希望你从这段视频中看到 可预览的 app 更容易理解 更易于测试 更易于维护 让我们来开发一些很棒的 app 谢谢
-
-
正在查找特定内容?在上方输入一个主题,就能直接跳转到相应的精彩内容。
提交你查询的内容时出现错误。请检查互联网连接,然后再试一次。