大多数浏览器和
Developer App 均支持流媒体播放。
-
SwiftUI 简介:打造您的首个 App
查看 SwiftUI 的实际应用!观看 SwiftUI 团队的工程师如何从头开始构建功能齐全的 app。了解这个新框架背后的理念,以及声明式编程的益处。深入探索 SwiftUI 的运作方式,并了解 SwiftUI 和 Xcode 11 如何一起帮助您更快地打造出色的 app。
资源
相关视频
WWDC20
WWDC19
-
下载
(引入SwiftUI 创建你的首个App)
大家好 我是Jacob Xiao 稍后 Kyle Macomber会上台 我们要跟大家分享SwiftUI 我们感到非常激动 它是创建更好、更快的app的 一种革命性的新方式
我们认为了解 SwiftUI的最佳方式 是实际创建一个app
使用SwiftUI 感觉像是魔法一样 但为了让大家明白 我袖子里没有藏东西 我想带大家一起看一下整个过程 在SwiftUI中 从零开始创建app
那么我们要做哪种app呢? 嗯 让我来为你打好基础 这里有人讨厌开会吗?
是吗?嗯 我们也是 但我和Kyle发现有些东西 可以让会议变得更能忍受 那就是在非常漂亮、 非常精致的会议室内开会
因此我们列出了一个 我们所能找到的最佳会议室的列表 并且我们想给它做一个app
那么让我们开始吧
好的 我要从创建新项目开始
我们把它命名为Rooms
并确保勾选了 那个Use SwiftUI
现在Xcode已经启动了 有一个视图 通过这个视图我们就可以 开始创建我们的UI了
我们的代码在左侧
右侧是Canvas
如果你熟悉Interface Builder 或Storyboard 你可能会觉得这并不新鲜 一侧有一些代码 然后另一侧 有可见的Canvas 对吗?
嗯 有一个非常重要的不同点 对于StoryBoards来说 你要在使用可视化编辑器创建更好 还是使用代码创建UI更好 之间做出选择 并且如果你选择了一个 然后稍后又改变主意了 那么你就要重新开始 但对于SwiftUI来说 完全不用担心这个问题 现在视图定义总会是Swift代码 你总是可以选择直接编辑代码 或使用可视化编辑器 并且你总是可以来回切换
因此如果我们在Canvas中 选中了某些东西 那么也会在代码中反映出 你所选择的内容 并且如果你修改了代码
那么这个修改也会在Canvas中 反映出来 它们无缝衔接 共同发挥作用
那么让我具体来讲讲该如何使用它 Canvas给我们显示了 视图代码的预览 它甚至帮助我们编辑和了解 那段代码 Xcode通过编译真实代码 并运行它来生成结果 从而显示这些预览 但对于预览我最喜欢的事情之一是 它们也可以使用 SwiftUI代码来创建 在这种情况下 代码在这里
稍后 我们将了解它如何为我们 提供那么大的能力 来自定义预览
现在我们的app 将会显示一个会议室的列表 那么让我们为列表创建网格吧
我要添加这段文本的下方 添加另一段文本 显示每间房间的更多详情
我只需要把它 拖动到我的Canvas上就能添加
并且Xcode甚至给我显示出 当我把它放在不同位置上时会怎样
现在当我释放它时 预览会更新以显示我的新文本 但更棒的是 Xcode实际上编辑了我的代码 以添加那段文本
Xcode把这些视图 嵌入到了一个Vstack中 从而获得我想要的布局
现在Vstack或垂直堆栈 是SwiftUI中 常用布局元素中的一种 它可以让你垂直叠加视图 还有一个Hstack水平叠加视图
并且这些堆栈都是容器 我可以在它们里边放入 任何我想要放入的视图
现在我要替换Xcode 为我提供的这个占位符 占位符内包含关于房间的更多信息
我们要显示它所能容纳的人数 现在 让我们使用一个硬编码的值
接下来 让我们在文本旁边添加一张图片
我可以在代码中进行编辑 就像在编辑器中一样简单 那么让我们在Hstack中 嵌入我们的视图 我可以命令点击我的视图 并选择嵌入一个HStack
然后Xcode更新了代码
现在我可以在Vstack旁边 添加一张图片 稍后我们要添加一些资产 但现在我只使用一个SF象征图像 让项目运作起来
SF象征是iOS 13中的新东西 它们是Apple提供的一大批图像 你可以在你的app中使用
我们已经有了一个基本版的网格 现在我要使用Canvas给网格 设计一个我想要的样式
我可以命令点击我的堆栈 并选择检查 用于了解我可以修改 这个堆栈的哪些属性
我要把它的对齐方式 设为leading
再一次 Xcode更新了我的代码 从而实现那个修改
现在我还可以命令点击我的文本 来检查它
让我们把字体变小点儿
我要使用子标题
对于Xcode修改代码的一件 很棒的事情 是它帮助我了解SwiftUI
比如它在这里添加了这段代码 来修改字体 我们把这种方法叫做修饰器 它们在SwiftUI中用于 自定义视图的外观或行为
我要在代码中添加另一个修饰器 用于设置前景颜色…
使它变成次生色
好的 现在网格做好了 让我们把它放到列表中
为此 我要命令点击我的网格
并选择转换为列表
这将把我的网格打包到一个列表中 并显示它的五次迭代
操作起来如此简单真是太棒了 这些代码就是在app中 显示列表所需要的全部代码 不需要什么委托或数据源 就是列表中的视图
接下来让我们把它与一些数据挂钩
我要拖入…
一些资产和我之前创建的一个 模型文件
现在我的模型有几个信息的字段 我们要使用它们
并在SwiftUI中的列表中 使用这个模型 我只需把这个类型变成可识别即可
这会告诉列表有什么东西进入了 并且我所需要的就是一个ID属性 而我们已经有了
现在这个模型文件还包含一些 测试数据 我可以用来调试app
现在让我们返回到我们的视图…
让我们传入数据
我要给房间…
添加一个属性
关于预览 其中一件非常棒的事 就是它们可以使用 它们自己的测试数据 因此我只需要在这里传入 我的测试数据
现在你可能已经注意到 预览上方出现了这个横幅
当我对键入内容做出重大修改时 比如在视图中添加房间属性 Xcode会暂停预览 直到我已经做好重新更新的准备 并且我只需要点击按钮来重新更新
接下来让我们使用数据来驱动列表 我们将向列表传入房间…
然后我会更新文本以显示房间名称
我们还要在文本中使用这里的功能
现在我们有真实的图片 对于图片 让我们使用房间缩略图
很棒
你可能已经注意到 列表中所发生的微妙变更 当我们这样做时 刚开始时我们的网格高度 是标准的44点 但当我们做了这些很大的修改之后 网格自动扩展了 从而确保能容纳那些图片 而不需要做任何额外的工作
非常酷
现在这些图片已经在情境中了 它们看起来有点儿格格不入 因此让我们给图片添加圆角半径 使用另一个修饰器
如果你不确定有哪些修饰器可用 你可以从修饰器列表中浏览和筛选 就在这里 在修饰器库中
让我们找到圆角半径 我们只需要把它拖到图片上
然后我们可以修改值 看起来有点儿太大了
很棒 现在我们的网格和列表 看起来都很不错 接下来我们要做的就是能轻触网格 以查看更多详情
为此 让我们把列表打包到一个 导航视图中
导航视图显示一个导航栏 以及一个可以叠加视图的堆栈
让我们也设置一个导航栏标题 我们只需要一些文本 比如说Rooms…
在导航栏中显示房间
现在让我们设置 要叠加到这个堆栈中的网格
为此 我要把网格内容打包到一个 导航按钮中
导航按钮有一个目的地 是一个要叠加到堆栈中的视图
首先 我们要使用一些文本 用于显示房间名称
然后我们要把网格的全部内容 放在那个导航按钮内…
然后就搞定了 请注意我们的UI已经进行了更新 从而自动显示这些UI指示器 SwiftUI会像这样 自动处理细节 因此我的UI在默认情况下 看起来就很棒
现在让我们也检查 网格用起来是否没问题 我只需要点击预览中的播放按钮 就会把我带到实时模式中
此时 Xcode会编译我的代码 并把它发送给模拟器 运行它并显示全部结果 就在Canvas中 意思是我可以与视图进行交互 因此我可以轻触一个网格来确保 一切都像我期待的那样压入和取出
并且如果我滑动 你会注意到SwiftUI会为我 自动提供一种高级行为 我们的网格保持突显 随着我的滑动 它们会交互性地不再突显 而我们不需要做任何额外的操作
好的 我们已经有了列表 并且它已经能用了 但这段视图代码现在变多了 并且我非常希望把网格分解出来 作为它自己的视图 Xcode通过一个简单的操作 帮助我实现了我的愿望 我只需命令点击我想要的视图 并选择提取子视图即可
很神奇
所有的视图代码都被提取到了这里 我甚至可以为新视图选择名称 让我们叫它Room Cell
谢谢
现在让我们给房间再添加一个属性
我们要把它传到这里
这是一个很神奇的流程改进 通过SwiftUI 视图变得非常轻量级 因此你再也不用担心创建附加视图 来得到更好的压缩或分离逻辑了
好的 接下来让我们创建详情视图
我要创建一个新视图…
并确保使用SwiftUI模板
让我们把新视图命名为 Room Detail
Xcode自动为我提供了一个 新ViewStruct 以及要创建它的预览代码
因为我希望这个详情视图 显示关于房间的更多信息 我要为房间添加一个属性
正如我之前所做的那样 我要更新我的预览代码 以便从房间的测试数据中 传入一个房间
然后让我们创建UI
我们使用一张图片… 并且要显示房间的图片名称
好的 现在我们显示了图片 但它对于视图来说有点儿太大了 默认情况下 SwiftUI会以内容的尺寸 显示全部图片… 从而防止图片产生缩放的视觉效果
但对于这样的照片来说 我们希望能把它的尺寸变小 我们可以使用一个图片专用的修饰器 叫做Resizable来实现
那么我只需把它拖到视图上…
好的 现在它已经重新调整了尺寸 以匹配我们所拥有的尺寸 但我们真的希望它保持原始长宽比 这次我们可以通过另一个修饰器实现 长宽比
长宽比修饰器可以让我 在.fill或.fit之间选择 .fill扩展图片 采用它的整个框架
.fit确保图片匹配到框架内
预览让我非常简明地看到和了解 这些不同模式之间的不同点 让我们使用.fit 这样我们能看到完整图片
让我们返回到列表… 并更新网格生成新详情视图
我们要创建房间详情视图
我们要传入当前房间
现在我要把预览切换回实时模式
我可以轻触网格查看图片
但现在我正在这里预览 我可以看到 我忘记在导航栏中设置标题了 因此让我们返回详情视图修改一下
跟以前一样 我可以用导航栏标题来设置标题…
我们要传入房间名称作为标题
但在我之前的预览中 我们只看到了视图自身 我真的希望可以快速验证 我所做的变更
因为预览可以使用SwiftUI 视图所拥有的全部功能 我们完全可以实现
我可以把预览设置为… 位于导航视图内… 使用我能在其它任何地方 使用的同样的代码 现在视图的预览已经处于导航栏内了
我看到它在这儿 对于详情视图来说 这个大标题看起来有点儿太大了 因此让我们更新一下导航栏标题 把显示模式设置为行内显示
好多了 现在当我选择一间会议室时 有一件事非常重要 房间必须得有一张好桌子 我不能集中注意力听某人在讲什么 如果桌子的质量不好的话
我可以看到这间房间内有一些桌子 但我实际上不能很清楚地看到 它的细节 但当图片是.fill时
我就可以近距离看到它很漂亮 看那种木纹
因此我希望能在.fill 和.fit之间来回切换 .fill可以查看房间详情 .fit可以立刻了解一切
但我到底该如何从视图内 修改这个长宽比的内容模式呢? 嗯 要了解如何实现 我们一定要了解视图是如何 在SwiftUI中运作的及为什么 因此我要邀请Kyle上台来谈谈 Kyle? 嗯 大家好 视图的运作方式
目前大家是怎么认为 SwiftUI的呢?
是的 我也非常激动
有点儿不可思议 那么在我们继续讲其它内容之前 我们要退一步 谈谈视图在SwiftUI中的 运作方式以及它为什么如此运作
那么我们刚才谈到了 实施Room Detail视图
在SwiftUI中 视图是一种遵守视图协议的结构 而不是和UI视图一样 是一个继承自基础类的类
这意味着视图不会继承 任何所存储的属性 它分配在堆栈上并按值传递
Room Detail 只存储了一间房间 那么它是一间房间的尺寸和比重 没有任何额外的分配或引用计算在内
在幕后 SwiftUI 把你的视图等级坍缩到 一个很有效的数据结构中 用于渲染 藉此 我们在SwiftUI中 可任意使用小且单一目标的视图 并且你们也可以
我希望你们从最后几张幻灯片中 了解的内容是 SwiftUI中的视图 是令人难以置信的轻量级视图 正如Jacob所说的那样 你永远不要犹豫 重构SwiftUI代码 因为提取子视图事实上不需要花时间
是的 你可以为此而鼓掌 非常棒 (一个视图定义了一个UI)
那么SwiftUI中的视图 和传统UI框架中的视图 实现的是同样的主要功能 它们定义一个UI
视图协议 只有一个要求: 一个body属性 它自身就是一个视图
在SwiftUI中 通过把小视图 组合到一起来创建大视图 我们创建了Room Detail 通过组合图片、 拥有原生分辨率的图片的视图…
可调整大小 可以让它适应任何一种规格 以及长宽比 一个能恰当地缩放 它的子视图的视图
对你所创建的任意视图的渲染 只是渲染它的主体
因此如果你设置了分割点 并且调试器停在那儿了
渲染的意思就是框架决定 它需要渲染视图
Tada
好的 那么视图— 框架知道何时取回视图的新渲染 因为除了定义UI之外 在SwiftUI中 视图还决定自己的依赖关系 (视图决定自己的依赖关系) 让我们扩展Room Detail 允许用户… 在适应…
和填充可用空间之间进行切换 从而可以让Jacob 看到漂亮的木纹
因此我们所需要的第一个东西 就是属性 但它是个特别的属性 它是一个状态属性 它表明图片是否被放大
当SwiftUI看到一个 带有状态变量的视图时 它会以视图的名义 为那个变量分配存储空间 在这个Memory图表中 绿色部分是你的app的内存 紫色是SwiftUI所管理的内存 那么放大变量的这个值 把你包含在内
因此如果根据那个变量 决定填充或适应
那么当它放大时我们会得到一个 像这样渲染的视图
当它不放大时就像这样子
那么现在我们所需要的 就是一个tapAction…
用于在两种状态之间来回切换 那么如果轻触 图片将被放大用于填充…
并被缩小用于适应
那么当我们轻触这里 到底会发生什么呢?
嗯 状态变量的一个特别属性 就是SwiftUI 可以观察何时被读写 因为SwiftUI知道这里的那个 Zoom 它是从body中读取的 它知道视图的渲染取决于它 意思就是…
当变量改变时 框架会再次请求body… 使用新状态值
因此这一次它会刷新渲染 以不同的内容模式
(事实在哪里?) 那么传统UI框架不区分 状态变量和普通的旧属性 但我发现这个区别非常清楚
在SwiftUI中 UI可能会处于的每个可能的状态 滚动视图的偏移 按钮的不突显
导航堆栈的内容 它衍生自权威数据 通常叫做事实来源 状态变量和模型共同 构成整个app的 事实来源
刚才我提到过对长宽比的调用 会生成视图
它的定义看起来类似这样
内容模式是一个普通的 旧Swift属性
你可以简洁地把每一个属性归类为 一个事实来源或一个衍生值
Zoom状态变量是一个事实来源
Content Mode属性 就衍生自它
回调SwiftUI 可以观察何时读写变量 那么当其中一个发生变更时 它就知道该刷新哪个渲染
框架通过请求新body 刷新渲染 重新生成一个新的长宽比视图 从而覆盖Content Mode 以及所存储的其它属性
这就是作用机制 SwiftUI中的全部衍生值 都保持是最新值
那么我们已经了解了 如何声明事实来源 通过状态变量
并且那个非常普通的旧属性 是一个衍生值 我们就不在这里举例说明了 但Swift为你提供了一个 叫做Binding的工具
它是一个传递读写衍生值的好工具 从技术角度来说任何常量 都可以作为完美的只读事实来源 正如我们在测试数据驱动预览时 所看到的那样
我还要指出另外一点 我刚才提到状态变量 和模型共同构成了 整个app的事实来源 那么我们有这个可绑定的对象协议 Jacob刚才用过了 从而告诉SwiftUI 要如何观察模型中所发生的变更
我们讲的可能很快 本周稍后会有一整场演讲 专门讲发展对不同流程基元的直觉
(管理依赖关系很难) 好的 让我们退回来接着讲
这与你在传统框架中 所做的非常不一样 传统框架中的视图自身非常固执 你最大努力地尝试 让它们保持是最新视图
你可能没从这方面思考过 当你使用传统UI框架时 但每一次视图读取数据时 它都会创建一个隐性依赖关系 它是一个依赖关系 因为当那个数据发生变更时 视图需要更新从而反映新值 如果它失败了 就说明有错误
SwiftUI会以你的名义 自动管理依赖关系
通过重新计算恰当的衍生值 因此再也不会出现那种情况
当然对于app中的单一依赖关系 我们不是只管理一次 我们的app很大 非常复杂
当提到你要在脑子里记住多少东西 以及犯错是多么容易 提到我们目前 手动管理依赖关系的方式 那非常困难 尽管我已经做了最大的努力 我所发布的每个app的每次更新 仍然存在UI错误 这些行上的每一个项 都是一个依赖关系
而这只是个开始
我觉得对于管理视图的复杂性来说 最困难的事 就是处理所有不同回调函数 可能被调用的顺序 这是一个老版 Room Detail视图 它是通过UIKit实施的
它与目前我们所看到的那个视图 拥有同样的功能 但它比那个还多一个功能
有时 当我们放大会议室图片时 桌子上的木纹太模糊了
因此我们决定添加一个特殊功能 使用神经网络和机器学习 因此我们可以在轻触时增强图片 在后台线程上
选择正确的会议室
好的 是的 过度设计了 是吗?
那么对于这个功能来说 只有一个问题 就是它有一个错误 我们得到了一份 关于偏离活动指示器的报告 它从不停止运转
这是怎么发生的呢? 嗯 原来如果事件按特定顺序发生…
我们忘记清理那个活动指示器了 在这种情况下 如果用户执行放大 并轻触增强按钮 它会消失 活动指示器会卡住 我们很容易犯这种错误 当你直接从事件处理器回调中 修改子视图时 而不是更新事实来源 而不是更新事实来源并从中衍生UI
我们犯这种错误是因为 当我们编写这段代码时 很容易只考虑到主逻辑 就是那些很容易记起来的东西
并且很容易忽略 很难记起来的其它东西 问题是随着视图要响应的 可能的事件的数量的增加 会暴露更多容易忽略的东西
假如你拥有全部四个事件 有多少可能存在的不同的顺序呢… 可能的传入顺序?
嗯 实际上有24种顺序 对于任意四个事件来说 实际情况更糟糕 因为每个事件可以出现一次以上 比如说用户多次轻触增强按钮
你们应该很熟悉这种情况 如果你们曾经实施过 可中断的动画的话 完成处理器可能会在一个 非常不期待的时间进入
如果我能告诉五年前的自己 关于我工作的任何事 那可能是UI编程非常难 就和无日志并发一样困难
你们中有多少人写过多线程代码?
很可能有2000人 是的 那么没有人认为它简单 我花了好几个月的时间来解决 某些多线程代码中的所有错误 即便这样我仍然不能 100%保证它的正确性
许多UI代码实际上就像那样 只不过不会发生崩溃 而是视图会出现在错误的地方 或者丢失 这是因为竞争条件和UI不一致性 分担了同样潜在的复杂性来源
这些容易忽略的顺序
我们大家都会使用的许多视图的 事件远远多于四个
你知道的 比如模型通知、 委托方法、目标操作、 完成处理器 这些都是事件 有12个事件的视图所产生的 可能的顺序大概是12的阶乘 几乎是五亿种
我们都使用过 拥有12个事件处理器的视图
那时你只能在你脑子里装太多东西
是的 这条虚线是 app中的一个视图
你们认为这两个点之间 有什么区别呢?
有人发言吗? 那些是报错 是的
随着新功能的添加 我们需要考虑到的 可能的顺序的数量 也会呈指数方式增长 如果我们忽略了其中一个
结果一样 不可避免
如今当你编程时 那么我想你们中有许多人凭直觉 找到了UIKit或AppKit中 处理这种复杂性的方式 就是把所有视图更新代码 收集到一个单一的方法中 然后从这些事件处理器回调中 调用那个方法 这可以很好地帮助你回避这种复杂性 SwiftUI从这个最佳实践中 受到了很多启发
如今当你以这种方式在UI中实施时 你需要考虑许多棘手的情况 比如从视图等级中添加或移除子视图 或堆叠和溢放导航堆栈 或更新表视图 我从来没自己独立 解决过这些情况 嗯 我们已经花了很多时间 做了很多工作 并且我们也以你的名义把这个 最佳范例编译到了SwiftUI中 视图协议只有一个要求:body 框架要调用一个单入口点 意思是只能以一种顺序调用
由于这种为发生变更的UI部分 简单地提取新视图的模式 SwiftUI释放了你的大脑 从根本上消除了UI不一致性
好的 让我们返回到演示中 并完成Room Detail视图 Jacob?
好的 让我们完成这个app
现在我们了解了 如何设置视图使其放大
我们添加了一个状态属性
并且我们把它命名为Zoomed 并默认把它设为假
然后在长宽比中… 我们要恰当地使用Zoomed 在填充(当放大时) 和适应(相反)之间变换
最后我们要添加tapAction 来切换那个Zoomed属性
现在让我们在实时模式中 试着运行一下
太棒了 我们可以在这两种 不同的模式之间切换
但还缺点儿什么东西 有人看出来了吗?
是的 它需要一个动画
对于SwiftUI来说 添加动画非常简单
我只需把变更打包到一个 对WithAnimation的调用中即可
现在变更有动画效果了
不仅如此 动画是完全交互式并可中断的 因此我总是可以 在不同的状态之间变换 并且它总是会显示在正确的位置
是的 很棒
接下来我要添加增强按钮 但事实是Kyle培训模型的方式 只能用于他所展示的那一张图片
因此我要添加一些更有用的东西 我们有一个伦敦的同事 我们经常想要了解哪间房间内 有视频会议 因此让我在详情视图顶部 为它添加一个图标
我可以用Zstack 显示堆叠的视图
我要把当前图片放到 那个Zstack内
然后我要添加另一张图片… 显示一个图标 表示房间是否有视频会议
我就用一张象征图像吧…
然后我们使用Video.fill
好的 这里有图标了 但它有点儿小
但象征图像会自动使用当前字号 调整自己的尺寸 因此我可以非常方便地通过修饰器 把它修改为一个较大好的字体
我们把它拖动到代码中 并把它应用到图片中
标题看起来不错 现在我们有大图片了 接下来我希望在左上方显示这张图片 让我们把Zstack的 对齐方式设为…
topLeading
好的 接下来让我们添加一些内边距 从而避开这个边界 再一次 我们可以使用修饰器来实现
我们只需把它拖动到系统图片上即可
看起来All Edges 是一个不错的选择
好的 很接近了 但我真的很希望让这个图标 一直显示在视图区域的顶部
我们可以通过把房间图片 放到一个灵活性框架中 实现让视频图标在那里显示 框架会伸展从而填充整个屏幕 当它有空余空间时 它会居中显示它的内容 这意味着房间图片 仍会在正确的位置显示
那么我要向图片中添加灵活性框架
我们要使用最小宽度为零 且最大宽度为无限大
高度也一样
好了 如果你想了解关于 SwiftUI中的布局 请查看SwiftUI演讲中的 创建自定义视图
现在我们有图标了 但我们只希望 当房间有视频会议时显示它 那么我们该如何做呢? 嗯 我们要用一个陈述性语法 非常简单 我们只需要使用一个if语句 那么我要添加一个if语句并检查 房间是否有视频会议 只有当它为真时才会显示图片
现在我们可以检查它是否是通过 修改没有视频会议的房间的 预览数据实现的 那很不错 但如果我们可以同时设置预览 来显示视频的多个版本更好
为此 我只需要把这个视图 嵌入到一个群中即可
现在这里有多个视图了
那么让我们创建第二个版本…
使用不同的测试数据 现在一个版本是有视频会议的视图 另一个版本是没有视频会议的视图
现在让我们返回实时模式
当我处于Zoom状态时 那个图标显示在图片顶部 有点儿分散注意力 所以让我们更新一下 放大时不显示它 我们只需要更新就可以实现
现在图标会在缩小和放大时 自动显示和隐藏 但更好的是它在有动画效果 淡入和淡出
并且我们还可以自定义动画行为 通过把它设置为一个不同的转换
让我们试试.move 并使用leadingedge
现在我们的图标会滑出和滑入
让我把它变成一个长动画 通过给自定义动画 指定一个两秒的时长实现
现在我们的动画更详细了
并且我们还能看到 如果我们轻触它会发生什么 当动画仍然在持续时 它转了个方向并立即返回
再一次 动画总是具有交互性 并且我们可以一直轻触它 它都将呈现正确的状态
那么这就是我们的详情视图 让我们回顾一下 我们刚刚创建了什么
我们的详情视图配置有一个 用于展示的房间 请记住它是一个衍生值 由父视图传入
我们还有状态属性 控制视图是否被缩小或放大
并且由框架持续化 并控制长宽比
然后我们还有视频会议图标
只有有视频会议的视图 并且视图没被放大时才会显示 我们还指定了它的转换方式 图标滑入和滑出
但在那个转换中到底发生了什么? 嗯 当图标被移除时 视频要动态地显示到新位置上 并且SwiftUI会等待 直到它完成动画 从而从等级中移除视图
当图标再次显示时…
SwiftUI在后台插入 并以动画形式把它移回去
我希望能长时间执行 AddSubview和 RemoveSubview动画 我们终于能实现了 我感到很激动
回忆一下 动画总是具有交互性 立等可用 这就是成为数据驱动 而不是事件驱动的闪光点 Kyle刚才谈到的全部事件 都可能会发生 因为这个是动态的 并且动画的开始和结束 有更多的事件 在事件驱动的世界中 创建一个这样的动画非常困难 但在SwiftUI中 只需要一行代码
现在让我们返回到房间列表 并完成这个app
随时间流逝 我们需要能修改会议室列表 因此让我们添加一些编辑支持 反正我们也在这里了 让我们也把数据模型 变得更真实一些吧 目前app中的数据是完全静态数据
我们有这一组房间 我们运作app的一切 都是我们一直有的东西
因此让我们更新模型 添加一个根存储对象 容纳我们的房间并可以随时修改
我要拖入一个预制模型文件 它含有一个房间商店
请注意商店是一个可变的对象 包含我们所需要的房间 为此我们所需要做的全部操作就是 当它发生变更时告诉SwiftUI
为此 我要让它遵守 可绑定的对象协议 它要求我拥有一个 DidChange属性
今年新增加了Combine框架 它包含许多组件 你可以用它把你的数据连接在一起 在这里我们要使用 传递publisher 它是来自Combine的 一个传递主体 为我们提供一个本地对象 我们可以订阅它 和给它发送更新 类似通知中心
然后… 我们要更新房间…
给它添加一个DidSet…
从而使我们可以在房间发生变更时 通知主体
如果你有兴趣 了解Combine的更多信息 本周有两场演讲 带你了解全部详细信息
现在让我们返回到视图…
并更新它 从而使用我们的商店
我们要把这个Rooms属性 修改成Store属性
我们要把它变成对象绑定的属性… 从而告诉SwiftUI 监测这个属性的变更 再一次 Kyle刚才提到的 SwiftUI的数据流演讲有许多 关于如何进行对象绑定 以及其它操作的相关信息
让我们也更新一下预览 从而创建新商店类型
使用我们的测试数据
我们还要更新列表 以便从商店外拖入房间
很棒 现在我们使用了新模型 无论何时当它更新时 视图都会创建一个新鲜的渲染
这意味着我们已经准备好 添加编辑支持了 让我们从向列表中添加一个 可以添加新房间的按钮开始
目前我们使用了单一集合来驱动列表 对于完全是数据驱动的列表来说 没什么问题 但当我需要更多时 SwiftUI还会允许我 在列表中和其它容器中 把静态和动态内容混合在一起
因此我可以用ForEach的集合 来替换这个列表
ForEach为它的每个集合项 都创建一个视图
因此现在我要在这个ForEach 旁边添加一个静态元素
我要进入库并找到一个按钮
然后我可以把它拖动到代码中 并把它添加到列表中
让我们更新一下它的文本 从而显示Add Room
让我们添加一个新方法 从而添加那个新房间
好的 我们要告诉Store 附加一个新房间
就让我们使用我们所在的房间吧
它叫做Hall 2 并且我认为大约有2000人
最后 让我们更新按钮 以使用新的Add Room方法
好的 现在让我们进入实时模式 尝试一下我们的新按钮
它在那儿 就在数据旁边 当我轻触它时 我们得到了新房间 很棒
那么就像那样 我们可以添加这个单一静态元素 在数据驱动的集合的旁边
SwiftUI可以 让我们简单地描述这些强大的组合 从而让创建复杂的列表UI 变得更加简单 再也不用担心索引路径报错了
但我想调整一下它的样式 让它看起来更好看一些 让我们把Add按钮和全部内容 都放到独立的分区中去
我们可以修改列表的样式…
让它分类显示…
然后就有了这种 很漂亮的分类显示的外观 然后我们要在按钮旁 添加一个分区容器
并在ForEach旁 添加另一个分区容器
很棒 现在我们有独立的分区
接下来让我们把删除与列表挂钩
我们要添加一个方法…
用来删除一组房间 我们会传入一些 表明在哪里删除它们的偏移量
然后我们就要告诉商店 移除那些带有偏移量的房间
然后我们在ForEach上添加 一个修饰器 叫做OnDelete
并且我们可以向那个修饰器 传入删除方法
现在如果我们返回到实时模式中
我们可以滑动其中一行并删除它 就像那样
这个ForEach现在的配置是 调用回调函数 无论何时当从中移除列表项时 然后我们的回调函数就会 调用Store来移除那些项 然后视图更新
好的 现在我们希望能把列表 放在编辑模式中 以防只允许滑动删除 那么让我们设置一个导航栏项 并且我们可以采用与设置导航栏 标题的同样的方式来实现… 用另一个修饰器
并创建Edit按钮 嗯 我们要创建一个Edit按钮
它在那儿 在我们的UI中
既然我们已经到这儿了 让我们再添加一个重排序支持
再一次 我只需添加一个简单的方法 来移动列表项
我们要把它从源…
移动到目的地
我们只需调用 Store的Rooms 并告诉它移动即可
好的 那么就跟以前一样 我们要添加另一个修饰器 这次是Move
并告诉它调用我们刚添加的方法
现在让我们再次运行app
我们可以切换列表的编辑模式 请注意所有的数据行都有编辑控件 但按钮却没有 SwiftUI会在需要编辑控件的 正确的位置上自动显示 而我们不需要做任何额外的工作
是的 很酷
当然现在我们可以拖动来重排序 请轻触列表项来移除它们
谢谢
那么让我们快速回顾一下 我们刚刚添加了什么
我们自定义了列表使其显示多个分区
并通过把它的列表样式设为 分类显示实现 并且我们把这个静态按钮 与来自Rooms的数据驱动的集合 混合到了一起
我们还了解了 如何仅通过这些修饰器 向列表中迅速添加编辑操作 以及几个修改数据的函数
还记得我们之前是如何 让房间类型变得可识别的吗? 嗯 ForEach自动监测 它集合中的变更 并合成正确的插入、删除 和修改 因此我们再也不用告诉列表 添加和移除行了 这意味着我们再也不用担心 数据源不一致异常了
是 对
那么这就是我们的列表 我们仅通过这段非常少的视图代码 制作了这整个复杂的列表UI
好的 现在我们可以 迅速创建好这个app了 但你可能会想 我们还是需要做很多工作 才能让它上线面对客户 这些天、对动态类型的支持、 深色模式、可用性、地点 这些都是app中所需要的东西
但对于SwiftUI 你可以自动获得这些行为的更多支持 我们可以使用预览来进行 非常迅速的测试
因此让我们打开预览 并添加更多视图
再一次 我要添加一个群 可以让我们拥有多个子群
然后我要添加视图的另一个版本
这一次我们要修改一下环境…
使用一个尺寸类别 它是一个较大的尺寸
让我们运行看看它是什么样子 一切都很棒 都是自动的
现在…是的
我们能免费得到这些太好了
现在环境是你 设置情境信息的一种方式 情境信息与视图等级中的视图有关 也是同时修改任意所包含的视图的 不同方面的一种方式 修改大量视图很棒 并且自定义预览也很棒 可以在不同的情境中浏览视图
让我们添加视图的另一个版本…
并更新环境… 把它的色系变成暗色
一切都没问题
最后 让我们再看看 其它语种的app怎么样
我有一些英文字符串文件 我要把它们拖进app中
并且我会告诉Xcode 对它们进行本地化
然后我进入我的项目文件…
导出一个阿拉伯语的本地化文件
现在让我们返回到视图中 再添加一个预览
现在如果我们先把布局方向 设置为从右到左
我们可以看到它会自动生效 不需要任何额外的工作
但如果我把区域也设为…
阿拉伯地区…
你可以看到一切都被本地化了
更棒的是 如果我们返回去看代码 我们不需要任何额外的操作 来支持这些功能 为了让文本可本地化 我们不需要标记 哪些字符串需要进行本地化 而哪些不需要 SwiftUI会自动推断 对于使用字符串文字的文本 它默认就是可进行本地化
对于那些通过传入字符串 而生成的文本来说 它默认就是按照原来的样子使用 这意味着app的全部文本 比如Rooms和Add Room 都能被本地化 但我们的全部内容 比如这些房间名称 仍会被正确地传入 但更好的是 你终于可以 使用字符串插值了 并让它们正确地进行本地化
我喜欢字符串插值 非常棒
我们很期待你们通过SwiftUI 开始创建app 你可以免费获得所有这些行为 你可以着重关注app的独特部分 并更快地为客户们创建更好的app
我们在这里创建的是一个 iPhone app 但你可以在全部Apple平台上 使用同样的API和技巧 并且你会在全部平台上 获得诸如此类的自动化行为
好的 让我们最后再看一次 我们的app 回顾一下我们创建了什么功能 并确保一切都运行无误
我是深色模式的忠实粉丝 因此让我们使用深色模式
好的 我们有房间列表 我们可以轻触其中一项 了解更多信息 并且在详情视图中 我们可以轻触使其放大到全屏显示 那会通过一个转换 隐藏我们的视频会议图标 并且那个动画总是具有交互性
并且我们还可以编辑列表做出修改
让我们把这个移动到下边…
我不认为有人会希望 使用像地牢一样的会议室
实际上我不确定Kyle为什么 会添加这个棘手的房间 让我们把那个也去掉
最后 让我们添加我们的会议室 太棒了 这就是我们的app
但我还要指出最后一点 是我们没有看到的东西 我们刚刚创建了整个app 并测试了全部行为 而一次也没有创建和运行app Xcode预览可以让我们浏览、 编辑和调试app 比之前的速度更快
那么谢谢大家 我希望你们跟我一样 喜欢用SwiftUI 很有意思
那么我们对SwiftUI的目标是 为你们提供一个最短路径 让你们创建 能满足最多受众的很棒的app
我们今天了解了SwiftUI的 四个主要设计原则 我们了解了它的陈述性语法 可以唤起你所创建的UI 并处理那些交互式细节 比如以你的名义 添加和移除子视图 我们了解了组合 以及如何把内容、修饰器和容器 组合到一起 来创建你所期待的那个UI
我们了解了你所得到的全部 自动化行为 我们不需要做很多 大部分都交给框架来实现 我们了解了SwiftUI如何 让视图保持一致状态 因此再也不会发生视图丢失 或出现在错误的位置上了 当然了 我们还了解了 SwiftUI的 很棒的可中断的动画 那是我最喜欢的功能之一 以前我总是感觉 必须在拥有闪亮的动画 和随之而来的毁灭性的复杂度之间 犹豫万分 再也不会发生这种情况了
SwiftUI的目标是 等一下 我们还没有再看一次app 它很酷 对吗? 好的 SwiftUI的目标是 以我们的名义替我们处理… 每一个app都需要的基础功能 因为当你创建app时 你添加的并不只是基础功能 你拥有特别的、独特的功能 那样才能让app成为 你自己的app 目标是替我们承担更多的责任 替我们分担每一个很棒的app 都需要的东西
让你把时间更多地花在让你的app 变得更特别的东西上
我希望你们都喜欢 我们对SwiftUI的介绍 请参加我们的讨论会 我们今天下午会在那儿 整个一周都会在那儿 我们还会主持其它演讲 UI数据流演讲 和创建自定义视图 学习SwiftUI的下一站 应该是SwiftUI要领演讲 我们将会讲关于语法的方方面面 以及一切你可以组合在一起 让视图原汁原味地呈现的东西 那么我希望能在那里看到你们 希望大家度过一个愉快的WWDC 并希望你们能享受SwiftUI
-
-
正在查找特定内容?在上方输入一个主题,就能直接跳转到相应的精彩内容。
提交你查询的内容时出现错误。请检查互联网连接,然后再试一次。