大多数浏览器和
Developer App 均支持流媒体播放。
-
向你的 SwiftUI app 添加丰富图形
了解如何通过 SwiftUI 让你的图形栩栩如生。我们将首先处理安全区域 (包括键盘安全区域),了解如何设计美观且不会与屏幕键盘重叠的无边缘缝隙图形。我们还会探索您可以在 SwiftUI 中使用的材料和振动功能,从而制作可轻松定制的背景和控件,并介绍 drawingGroup 和全新画布等图形 API。使用这些工具,你可以比以往更轻松地在 SwiftUI 中设计完全交互式和可中断的动画和图形。
资源
- Add Rich Graphics to Your SwiftUI App
- Adding interactivity with gestures
- Composing SwiftUI gestures
- GestureState
相关视频
WWDC21
-
下载
嗨 我是雅各布 欢迎来到 《为你的SwiftUI应用程序 添加丰富的图形》 我正在与几个同事一起 开发一个构建渐变的应用 今年 颜色是我们团队的热门话题 大部分的工程都已经完成 现在我只需要通过 添加一些丰富的图形来完成它 在我们自定义应用时 我们将看到几个不同的区域: 安全区域 包括对其进行定制化 新的前景样式支持 一套丰富的新材料; 并使用画布进行绘图 这是一种强大的新视图 让我们开始吧 我将向你展示应用程序中目前的内容 我们有一个渐变库 我可以查看这些渐变 其中有我很喜欢的东西 只是我不能把手指放在它上面 我还可以编辑渐变… 这让我可以改变颜色停驻点
我可以添加新的渐变
我也可以在一些可视化工具中 使用这些渐变… 但是一次一个步骤 我们稍后再看
现在 让我们聚焦在这个渐变细节视图 它是功能性的 但我们的实际内容很少 相对于镀铬和空白空间 我希望渐变真正填满这个屏幕 所以让我们开始在Xcode中编辑它
这是我们的主要细节视图 它也用于我们的编辑模式 先从isEditing false开始 稍后再来看编辑模式 让我们删除这个框架 让我们的渐变 尽可能多地使用空间
现在渐变占据了 所有的高度 我们不再需要这个spacer了
我们可以进一步将控件 放在这个渐变之上 通过将其更改为ZStack
如果你以前没见过ZStack 它会在彼此之上 列出元素 而不是彼此相邻 让我们将编辑控件移到底部
而且我们只需要在控件上填充 而不是在我们的渐变上… 所以让我们移动这个
你可能会纳闷 为什么渐变的顶部 和底部还有空白 即使我们删除了填充 在默认情况下 SwiftUI会将你的内容 放置在安全区域内 避免任何会遮挡或裁切 你的视图的东西 例如主页指示器 或正在显示的任何长条 安全区域是做为 从最外面的完整区域插入的范围 显示视图的地方 安全区域中的内容 会自动布置在适当的插图中 避免那些会被遮挡的区域 安全区域也是 SwiftUI如何帮助你避免 在键盘下绘制内容 所以在我们的应用中 我们的控件会自动升起 远离键盘
这工作的原理也是相同的 如果我们更仔细地研究 因为有多个不同的安全区域 最常见的是容器安全区域 它由容器驱动 其中显示着一个视图 并包括长条和设备镀铬之类的东西 此外 还有一个用于避开键盘的 键盘安全区域 请记得键盘安全区域 位于是容器安全区域内的范围之内 除了键盘之外 它可以作为 容器安全区域 保护内容的安全 你可以选择退出安全区域 通常你不需要这样做 因为大多数内容应该在安全区域内 所以没有被裁切掉 到头来 这还是安全的 但是忽略安全区域 对于背景或其他内容是有意义的 如果你想要完全贴合边框的话 你可以使用此代码选择退出 所有安全区域 或指定键盘区域 来选择退出键盘安全区域 让我们将ignoresSafeArea 添加到我们的线性渐变中 以获得全出血效果
这个编辑按钮在我们的渐变顶部 不是很明显 所以让我们只忽略 底部边缘的安全区域
现在 为了确保我们不会遇到 由于渐变导致底部文本 难以辨认的相同问题 让我们在它后面添加一个背景
我们稍后会再自定义背景 但让我们从最简单的默认值开始 这为我们提供了在暗色模式下 自动更改的白色背景
而这个背景也会自动延伸 到安全区域之外 这个版本的背景及其行为在 iOS 15和匹配的 操作系统版本中是新的 来看看它是如何运作的 让我们从容器视图 及其安全区域开始 我们有了我们的内容视图 它将在安全区域内 以保持清晰易读 如果我们天真地向它应用的视图 添加了具有相同边界的背景 我们就会得到这个 但如果我们将 ignoresSafeArea修饰符 应用于背景视图 然后它将扩展到安全区域之外 同时保持主要内容的美观和安全 新的背景修饰符 会自动给你这个行为
让我们回到我们的背景 并开始自定义它 我们可以传入特定的样式 可以是颜色或任何其他样式 例如渐变
这在这个应用中没有意义 但来看看一些柔和的颜色
我还可以传入一个形状 来将此背景裁切成… 例如说 圆角矩形
请注意 当我使用自定义形状时 背景不再延伸到安全区域之外 所以形状会与你的内容边界相匹配 我认为更适合我们的应用程序的 是模糊我们的背景 我们可以使用另个新的API 来做到这一点:材料 材质是一组你可以应用的 标准模糊样式
让我们让这个背景回到 占据整个区域
材料非常适合 我们想要展示 像这样的彩色内容的地方 你可以选择一组不同的材料 从超薄到超厚 所有这些都会自动 在每个平台上显示正确的设计
我将在这里使用薄的材料
接下来 我想自定义我们的文本 让我们来让颜色的数量 不那么突出 来凸显名称是此处的主要信息
我可以通过设置次要的前景样式 来做到这一点
你可能已经注意到 次要内容 会自动显示一种 称为Vibrancy的效果 它混合了它背后的颜色 在SwiftUI中 这种效果没有额外的 API 当你在材质上下文中 通过Quaternary样式 使用新的Secondary时 它会自动发生 当你明确地添加带有材质的背景时 就会发生这种情况 就像刚刚那样 或者当你的内容位于系统组件中 如侧边栏 它会为你添加材质
而这些样式具有很多自动智能 他们会自动做正确的事情 在非模糊环境中使用的时候 在他们不使用鲜艳效果的地方 当你给它们设置颜色时 它们也会自动改变它们的行为 设置每个级别的颜色版本 并且同样的支持适用于 设置任何基本的前景样式 甚至像渐变之类的东西 请带有品味地使用 需要注意一件事: 任何给定的文本都可以 应用单个前景样式 但可以在其范围内应用多种颜色 例如 我可以使用字符串插值 来嵌入内部文本…
然后应用红色的 foregroundColor… 到“颜色”这个词 它将显示该颜色 自动选择退出该范围的鲜艳度
更重要的是 有了这些前景样式 有史以来第一次 你可以通过嵌入的表情符号 获得正确的行为 而且效果很好
这看起来很不错 让我们再次运行它 并尝试在编辑模式套用这些更改
效果几乎都不错 这些颜色在模糊之下还不错 这很棒 但如果你仔细观察 它做的事并不完全正确 当我一直向下滚动时 有一点隐藏在模糊后面的列表 让我们更仔细地看看发生了什么 让我们去掉铬 只看相关视图 你看
如果我们将这些视图水平滑动一点 我们可以看到这是因为长条 只是在内容之上的ZStacked 现在我们想要看到后面的所有视图 这不是正确的行为
我们可以在这里更改为VStack 但少了模糊下的列表 在向下滚动时 我们不会 显示任何颜色 我们想要列表的背景 及其可滚动区域 延伸到栏下 但不是其主要内容 而这正是安全区的用途 如果我们让安全区域被这个栏插入 那么任何重要的内容都不会被遮挡 为了自定义我们自己视图的安全区域 我们可以使用一个新的修饰符 safeAreaInset 这让我们可以添加辅助内容 比如我们的长条 一路到主要内容 我将替换我们的ZStack…
使用safeAreaInset…
使用.bottom 的边缘…
并将我们的控件放在其中 让我们再次运行它 来检查一下
这个视图看起来还是一样的 这很好 那是因为它忽略了安全区域
而在编辑模式…
我们仍然可以在栏下滚动 以获得模糊效果 但是当我们滚动到底部时 没有任何东西被遮挡 太好了 接下来 让我们看看我们的可视化器
让我们从已经编写好的 Shapes可视化工具开始
它显示了大量随机形状的符号 每个符号都使用 应用程序中的一个渐变绘制 我可以点击一个符号来拉近它…
或点击背景 来重新定位所有符号
如果你之前看过我们的 SwiftUI动画演示 你就会知道它始终具有 交互性和可中断性 所以我可以继续重新排列…
甚至在发生这种情况时 点击来选择和取消选择形状
如果我去看代码…
它使用一种通用技术在 SwiftUI中绘制图形 有一个GeometryReader 所以我可以读取视图的大小 布置所有这些图形和一个 和ZStack来帮助我定位它们
在本体的最后 有个你之前可能见过的修饰符 drawingGroup drawingGroup会告诉SwiftUI 组合它所包含的所有视图 在单个图层中绘制 这适用于像这样的 图形元素 但不应与文本字段和列表等 UI控件一起使用
当你想显示大量图形元素时 这是一种很好的技巧 就像我们在这做的那样 而drawingGroup的好处之一是 即使这些视图的绘制方式不同 你仍可以使用 SwiftUI中的相同功能 和你在应用程序中的其他地方一样
例如 在这里 我们对每个符号应用了一个手势 用于点击它们 在我们更改选择 或重新定位它们时 所加入的动画 这些视图中包含的可访问性信息 也正常地传递了… 例如 每个符号上的这些 可访问性操作 但是 为了支持所有这些功能 每个视图会需要一些簿记 和存储空间 如果你有足够多的元素 那么即使是额外的虚耗也可能太多 对于这些情况 我们引入了新的画布视图 我们的下一个可视化器 将展示一个复杂的粒子系统 而这还没有写好 让我们来建造它吧 让我们从画布视图开始绘制它
这让我们可以创建一个 每次绘制画布时运行的闭包 并包含我们的绘图命令 如果你熟悉UIKit或 AppKit中的drawRect 这原理非常地相似 这个闭包为我们提供了一个上下文 这是我们向其发送绘图命令的地方 以及我们可以用来 获取整个画布大小的尺寸 让我们从绘制图像开始
我可以利用我在 其余的 SwiftUI代码中 所使用的相同图像类型 来创建一个
让我们告诉上下文来绘制我们的图像
当我们在0,0处绘制它时 它在这里 以原点为中心 不是很明显的地方 由于我们有整个画布的大小 这次我们转而利用它 在中间绘制它
如果我将预览更改为暗色模式 你会看到这件事…
我们的图像自动翻转为白色 因为它使用相同的前景样式 就是我们之前看到的 由于我们要构建一个粒子系统 让我们再画几次这个图像
记得 此闭包是用于命令式代码 它不是ViewBuilder 所以为了循环 我可以使用常态
让我们稍稍移动每一个图像 才能实际看到它们
现在 我们多次绘制此图像 但每一次 上下文需要解析它 根据当前环境等事物对其进行评估 即使每次都是相同的图像 我们可以通过在绘制之前 自己解决图像 来改进这一点
现在我们有更好的性能 因为我们共享相同的解析图像 但是解析后的图像还可以 让我们做一些其他的事情
我们现在可以要求它的大小和基线 在我们的例子中 我们将利用它的尺寸 依适当的量来移动每一个
接下来 让我们 在闪光后面添加椭圆 我要把它们画在同一个范围 所以让我们拉出一个框架 来把它们都画进去
我将创建一个具有相同 X和Y值的CGRect 并使用我们的imageSize 作为宽度和高度…
然后在该框架中绘制我们的图像
因为每次绘制操作都是按顺序完成的 为了把我们的椭圆放在图像后面 我们需要先绘制它 我们可以用context.fill 来绘制它…
这需要一个路径和一个阴影 你可以使用标准贝塞尔曲线构建路径 但这里有一个提示: 你还可以使用椭圆等形状 并询问它们在给定矩形中的路径
另一个参数是阴影 它是我们路径的填充物 这能使用与我们SwiftUI应用的 其余部分相同的样式 让我们使用青色
还有椭圆 不过 与图像之间的对比并不多 让我们来解决这个问题 图形上下文具有许多标准绘图属性 例如不透明度、混合模式、变换等 让我们在这里设置一个不透明度 我们可以看看这个 上下文表现得和你习惯中的 可能有点不同的区域 如果我只是在上下文中 设置了不透明度 那么它的行为就会如你所愿 它会影响之后发生的所有操作
在以前 如果我想对图形上下文进行更改 只适用于我的一些绘图操作 我必须在保存和恢复调用中 将这些操作括号起来 但是对于SwiftUI上下文 我所要做的就是对副本进行更改
这些更改仅影响 使用修改后的上下文完成的绘图 在原始上下文中完成的绘图 则不受影响
让我们也为我们的图像添加一些颜色
我们可以对解析的图像 做的另一件事是 设置阴影来控制符号的绘制方式
让我们在这里设置蓝色
看起来比我希望的要少一些 有时在绘图时 正确的混合模式会有大大的不同 混合模式控制颜色的组合方式 尤其是部分不透明度 就像我们在这所看到的
来设置一个屏幕混合模式 这结合了颜色 使它们永远都能变得更亮 那样看上去更好
还有更多可以做的绘图操作 请看GraphicsContext 来查看所有可能的内容 现在 为了让它像模拟一样 它需要可以实际移动 在SwiftUI中有一些工具 能随着时间的推移做出一些改变 动画是最常见的 它们通常只是 在你做出改变时自动发生 今年 我们要介绍一个新的低层级工具 叫做TimelineView 让你可以在想要精细控制 事物随时间变化的程度时使用 我可以通过将它包裹在 我想改变的视图周围 来使用TimelineView
我能以用时程表来配置它 告诉它多久更新一次
我们有像定时器之类的时间表 但我们将使用动画时间表 来尽快在我们显示它们时获得更新
如果你熟悉显示链接 则其工作原理非常相似 如果你不熟悉 那也完全没关系 我们接受了一个时间线上下文 而它提供了该展示什么的信息
我可以在几秒钟内抽出一个时间 来利用它为我们的图像制作动画
让我们的图像在旋转振荡中移动吧 所以我会根据当前时间 做出一个角度
让我们通过余数 让它每三秒循环一次…
然后乘以120 得到360度
我们将得到余弦的X值 还是正弦? 希望你还记得三角函数
现在让我们使用该值 来更改我们的偏移量…
并实时预览我们的视图 看看它会变怎样
很好 看看当它们重叠时 会怎么变得更亮? 这就是我们运作中的屏幕混合模式 接下来 让我们添加一些交互性 稍早 我们研究了 通过向单个视图添加手势 可以实现的一些交互 请记住 使用画布 的代价之一 是其中的各个元素 会组合成一张图 因此 我们无法 例如说 将手势附加到这些单独的图像上 但我们仍然可以向整个视图添加手势 让我们来添加 增加显示的闪光数量的能力 我们要为显示的数量添加一些状态
让我们从两个开始 使用计数来控制我们的循环
然后我们添加一个 TapGesture来增加计数
来更新我们的预览吧
现在我们可以通过点击添加闪光
使用画布另一个重要的方面是 由于它是单个图形 没有任何与内容相关的信息 可供辅助功能使用 为了使其可被访问 我们将使用标准的可访问性修饰符 添加有关我们视图的信息
对于更进阶的情况 有一个新的强大的 .accessibilityChildren修饰符 允许你指定 一个SwiftUI视图的层次结构 来用于生成有关视图的辅助使用信息 请参阅 《SwiftUI辅助功能:超越基础》 来得知有关使用它的更多信息
我们已经建立了一个相对简单 使用画布的方法 但它是被设计来支持更为复杂的使用 所以我们得给事情加一点料 我的一位同事 为我编写了一些模拟代码 其工作方式与我们这里的 工作方式相同 但是有更多元素来做更有趣的事情 我有他发给我的文件 我将把它粘贴到我们的视图中
这段代码与我们刚才所做的 具有相同的结构 我们现在有个长期存在的模型对象 而我们会随时更新 来持续追踪我们所有的粒子 我们有相同的 TimelineView和画布 来制作动画和绘制我们的内容 我们正在更新模型的日期 设置屏幕混合模式 并告诉每个活动粒子 以和刚刚我们在椭圆上 相同的方式来绘制自己 最后 我们应用了相同的修饰符 只是手势稍微复杂一些 那么来看看它是什么样子
它会定期创建新的烟花爆裂 我们也可以点击来添加更多爆裂 它们是使用应用中的 渐变颜色的椭圆制成的
在画布中绘图的另一个好处是 它也适用于watchOS tvOS和macOS 在所有SwiftUI平台上皆可使用 好的 我们完成了我们的应用程序 在此过程中 我们研究了使用和修改安全区域 如何使用前景样式 来控制内容的绘制方式 如何使用材质来获得模糊和鲜艳度 我们使用画布和TimelineView 来构建复杂的动画图形 我等不及想看看你在应用中 构建了哪些美妙的图形 [音乐]
-
-
3:53 - Ignoring safe areas
// Ignore all safe areas ContentView() .ignoresSafeArea() // Ignore keyboard only ContentView() .ignoresSafeArea(.keyboard)
-
7:08 - Foreground Styles
VStack { Text("Primary") .foregroundStyle(.primary) Text("Secondary") .foregroundStyle(.secondary) Text("Tertiary") .foregroundStyle(.tertiary) Text("Quaternary") .foregroundStyle(.quaternary) }
-
7:35 - Purple Foreground Styles
VStack { Text("Primary") .foregroundStyle(.primary) Text("Secondary") .foregroundStyle(.secondary) Text("Tertiary") .foregroundStyle(.tertiary) Text("Quaternary") .foregroundStyle(.quaternary) } .foregroundStyle(.purple)
-
7:41 - Blue Gradient Foreground Styles
let blueGradient = LinearGradient( colors: [.blue, .teal], startPoint: .leading, endPoint: .trailing) VStack { Text("Primary") .foregroundStyle(.primary) Text("Secondary") .foregroundStyle(.secondary) Text("Tertiary") .foregroundStyle(.tertiary) Text("Quaternary") .foregroundStyle(.quaternary) } .foregroundStyle(blueGradient)
-
-
正在查找特定内容?在上方输入一个主题,就能直接跳转到相应的精彩内容。
提交你查询的内容时出现错误。请检查互联网连接,然后再试一次。