大多数浏览器和
Developer App 均支持流媒体播放。
-
优化 Mac Catalyst app 的界面
通过使用 Xcode 中新的“Mac 版优化界面”选项,探索如何定制你的 Mac Catalyst app,使其在 Mac 上看起来更像是在家中一样。探索 Catalyst app 的新布局和外观选项,并了解它们如何为你提供图形性能提升,更清晰的文本以及专门为 Apple 台式机和笔记本电脑设计的界面。我们将向你展示如何利用这些选项,并提供在为多个平台开发时组织代码的最佳做法。 积极从事 Mac Catalyst 项目的开发人员将充分利用本节内容。如果您是 Catalyst 的新手,我们建议您观看“设计 Mac 版 iPad Apps” 和“ 推出 Mac 版 iPad Apps ”入门。 有关使用 Mac Catalyst 的更多信息,请查看“Mac Catalyst 的新功能”。
资源
- Adding Menus and Shortcuts to the Menu Bar and User Interface
- Human Interface Guidelines: Mac Catalyst
- Mac Catalyst
- Optimizing your iPad app for Mac
相关视频
WWDC21
WWDC20
WWDC19
-
下载
(你好 WWDC 2020)
你好 欢迎来到 WWDC (优化 Mac Catalyst app 的界面) 你好 我是 Nick Teissler 我是一名 Cocoa 工程师 等会儿 我的同事 Jake Carter 会加入我们
今天 我们会告诉你 如何优化 Mac Catalyst app 的界面
Mac Catalyst app 的下一阶段被称为 “为 Mac 优化”
为 Mac 优化能够让你获得 类似于 Mac 的视觉效果和控件 这些是缩放匹配 iPad 的 Catalyst app 所没有的东西 (提供改进的视觉效果和控件) 选择为 Mac 优化是构建设置的改变 它会在启动时生效 (在 Mac 惯用法里运行你的 app) 为 Mac 优化的 app 会在 Mac 惯用法里启动 缩放匹配 iPad 的 Catalyst app 会在 iPad 惯用法里启动
这个设置不会出现任何编译影响 所以当你在为缩放匹配 iPad 的 Catalyst app 优化时 你不会遇到构建错误或框架问题
我们推荐你先从已经为 Mac 进行过 一些改动的 Catalyst app 开始 在为 Mac 优化之前 你需要做很多事 比如为菜单栏操作增添键盘快捷方式 或者用 UIContextMenuInteraction API 处理你界面的右击 (增强现有的 Mac Catalyst app) (将 iPad app 带到 Mac 的下一阶段 为 Mac 设计 iPad app) 请观看这些来自 2019 年全球开发者大会的环节 看看在为 Mac 优化界面之前 你可以添加到 Catalyst app 的 审查功能 让我们来看看什么是“为 Mac 优化”吧 我们到底要优化什么呢? (什么会被优化?) 为 Mac 优化的根本在于 获得类似于 Mac 的视觉效果 当你把你的 iPad app 转变成 优化了的 Catalyst app 之后 我们会按照一比一的比例渲染内容 (内容比例) 当 Catalyst app 被调整 以匹配 iPad 视图时 内容比例会被缩减至 77% 在这种情况下 你代码里的 100 分 在屏幕上就会变成 77 分
当你为 Mac 优化时 代码里的 100 分就永远都会是 屏幕上的 100 分 不缩放内容会为你 app 里的 视觉增强铺平道路
(macOS 系统控件) (按钮) 如果你使用 UIButtons、UISlider、UISwitch UIActivityIndicator 等控件的话 这些都会被渲染成 macOS 系统控件 在这里 你可以看到 iOS 系统按钮 在优化时会被渲染成 Mac 系统按钮
这些新控件的外观会有着 匹配 Mac 上控件的 大小和度量 它们很有可能不会 和你 iPad 上的外观一样 所以你的布局可能会有所改变 (macOS 字体度量) 为 Mac 优化的 Catalyst 会调整 字体样式大小 比如正文、标题和标注以匹配 Mac 和匹配 iPad 的 Catalyst app 不一样 渲染的文本不会再被缩放了 字体样式大小会有所改变 (17 号 - 13 号) 比方说 缩放匹配 iPad 的 Catalyst app 的 正文字体样式是 17 号 而为 Mac 优化的 Catalyst app 则是 13 号 真正的字体大小看起来 会明显比缩放的字体大小更锐利 (硬编码大小不会被调整) 这也意味着如果你有硬编码字体大小 它们是不会被调整的 你的 UILabel 或 UITextField 可能会感到很难过 一小部分的它们 大约有 23% 吧 不会在 Mac 上感到宾至如归 (macOS 标准间距)
最后一项较为直接的改变就是 如果你是在用系统间距开发限制 自动布局会直接采用 Mac 间距 一般上来说 Mac 的系统间距会比 iOS 的大 (什么会被优化?) 如果你选择为 Mac 优化 你的 Mac Catalyst app 这些就是你会获得的高阶功能 (缩放 - 优化) 在不断增加的 可让你把 app 引入 Mac 的方案里 你可能需要点时间在脑海里 为 Mac 优化的 Catalyst 定位 为 Mac 优化的 Catalyst 是缩放匹配 iPad 的 Catalyst 的另一选项 你无法两者兼有 让我们更直接地比较一下它们吧 (优秀的 Mac app) 首先 它们并不会淘汰彼此 缩放匹配 iPad 可能会更适合你的 app 而我们也会不断地看到 在这两种惯用法上运行的优秀 app (转变成 Mac app 的快速通道) 缩放是转变成 Mac app 的快速通道 它会让你的 app 在 Mac 上运行 你不用为了让你的 app 在 Mac 上看起来更好 而重构你 app 的所有视图 这也是缩放 app 背后的理念 你只需要做出一些调整 就能快速转换并运行你的 Mac app (缩放 app 潜在的下一步) 根据 app 的细节 你所做出的调整 可能合意 也可能不合意 为 Mac 优化是潜在的下一步 (偏向 iPad 兼容性) 缩放 app 偏向 iPad app 的兼容性 Catalyst 会尽它所能 让你无需为了在外观上兼容 Mac 而更改 app 的代码 (偏向真实的 Mac 外观和感觉) 优化 app 偏向真实的 Mac 外观和感觉 Catalyst 会采用 Mac 的控件 和未缩放的字体大小 让 app 的界面在 Mac 上看起来完美无比 (保留布局) 由于缩放 app 偏向你 iPad 版本 app 的 兼容性 因此你的布局将尽可能地被保留 在大多数情况下 你无需为了让 app 在 Mac 上的布局看起来正常 而做出任何改动 (需要进行布局工作) 鉴于优化会带给你完全不同的控件外观 因此你需要进行一些布局工作 你需要对控件大小做出假定 现在我们知道了哪些东西会被优化 也了解了两种惯用法的分别 是时候开始想想哪些 app 会在 选择优化后赢得最大的收获 哪些 app 想要被优化 以便能够呈现出最好的一面? (怎样才算是理想的对象?) 那些显示大量文本的 app 可以明显地看到视觉上的改进 这是因为它们获得了真正的字体大小 而不是缩放了的字体大小 (大量文本) Swift Playgrounds 是今年在 Big Sur 为 Mac 优化的 Catalyst app Swift Playgrounds 的富文本编辑 驱动了 app 的视觉效果 因此为了这些效果 而进行一些布局工作其实是合理的 (突出显示图形) 突出显示图形的 app 也会有改进的潜能 (SceneKit 或 Metal) 根据我们的经验 使用了 Metal 或 SceneKit 的 密集图形 app 会看到显著的改进 Swift Playgrounds 在他们的 《学习编码》系列里使用了 SceneKit 在为 Mac 优化之后 Swift Playgrounds 在集成和离散图形系统的 Mac 上 都获得了更高的帧率和更低的功耗 (自定义图案) 另一种突出显示图形的种类就是 高度自定义的精致图案 缩放 Catalyst 资产和优化资产的分别 和你缩放一个 png 文件到指定大小 而不是缩放到自然尺寸时 所看到的视觉差异是一样的
通过左右对比两种版本 你可以快速评估你 app 的显示质量 (加利福尼亚州科学院) MapKit 里的图案是很好的例子 MapKit 有着全世界 最棒的自定义设计和图标 看一下放大版本的 完美像素地名标注 就能看出缩放和未缩放图案之间的区别
左边的是缩放了的 加利福尼亚州科学院图标 右边的则是未缩放图案 你仍然可以看清 这座建筑物有多少窗户和柱子 有些精致的细节会消失在缩放图形里
接下来是笛洋美术馆 (笛洋美术馆) 在这里也是一样 未缩放资产 和它的字体看起来都更锐利一些 (所罗门R古根海姆博物馆) 最后则是古根海姆博物馆 圆形建筑物 在未缩放的 MapKit 资产里显得更完整
图标在屏幕上渲染得比较小 因此对用户来说 那些视觉差异 并不如这里显示的大 (巴黎) 但这件事却在 MapKit 上 得到了扩大的运用 在像巴黎这样的密集城市里 放小地图来看的话 能够具备显示精细道路网的分辨率 是优化 Catalyst app 的绝佳动力 如果你大量使用 MapKit 的话 (控制视图) 另一个可以让 app 成为理想对象的特质 就是拥有多个控制视图 就像是会显示可调整数值的气泡弹出框 如果是为 Mac 优化的话 这个视图会看起来更好看 地图利用了这点 在这里使用了复选框 比滑动开关更适合放在这里 最后 你的第三方依赖性状态 可能会是限制你 app 成为 理想对象的因素 对于框架开发人员来说 支持 Catalyst 对象可能还不足够 尤其是如果你的框架 是专注于用户界面的话 你很有可能需要支持为 Mac 优化 以便不会限制你的客户 等下 Jake 会教你如何更改代码 来支持 Mac 惯用法 (缩放匹配 iPad) 现在 我们来看看为 Mac 优化 如何改变缩放匹配 iPad app 的视图 左边显示的视图是馅饼食谱的介绍 这个布局是由多层视图所构建的 并且包含了五种元素 一个按钮、一个标签、一个滑块 一张图片和一个文本视图
仔细看 我现在要在不做出布局调整的 情况下 为 Mac 优化这个视图
(为 Mac 优化了) 看来布局还不错
你注意到的第一个变化 可能是左边的 iOS 系统按钮 变成了右边的 Mac 系统按钮 这造成了按钮在运行时拥有不同的大小 根据你布局视图的方式 也可能会有不同的原点
让我们来看看这两个窗口吧 它们在 Mac 屏幕上的大小是完全一样的
但请记住 左边窗口的大小和它的内容 实际上只是你 app 代码里的 77% 大小
这是 Catalyst 在 Catalina 上 第一次发布时的形态 但随着我们继续探讨 其他的改变时 请你牢记它
接着 我们来看看窗口的前缘 左边窗口前缘和文本视图之间的空间 明显比右边的更小 就和我们之前看到的一样 系统间距值通常在 Mac 上会比较大
这也意味着 为 Mac 优化的视图会有更多的空白边距 这会让视图拥有更少的宽度编排内容 这也是为什么文本增加了行数 并且滑块看起来更窄的原因
但很奇怪的是 即使宽度减少了 在为 Mac 优化之后 图片视图依旧占据了更多空间 它以必要的宽高限制呈现 匹配自身的内容大小
明白了这点之后 你就可以猜到 它为什么会在为 Mac 优化之后变得更宽 因为图片从 77% 的自然大小 变成了 100% 的自然大小
你可以根据你的情况 采取不一样的方式处理 最好的方法就是使用资产目录 提供特定于 Mac 或 iPad 的资产
如果你没有适当大小的资产 你可以使用 Interface Builder 的特质变化 来分配不同的大小到其中一种惯用法上 这完全解释了滑块的外观变化 滑块被配置来占据剩余的可用空间 增宽的边距和图片 减少了滑块的可用空间
事实上 为 Mac 优化的滑块 所缩短的距离是增宽边距 和增宽图片的总和
我们说够布局了 现在说回外观吧
左边的滑块是没有定制过的 系统用户界面滑块
右边的是 采用了新设计的 macOS 滑块
你仔细看看轨道高度 和旋钮阴影的细微差别 对你和你 app 的用户来说 很重要的一点就是 这个滑块和 Mac 上的其他滑块是一样的 在这些截图里 不太明显的一件事就是 文本渲染的差别 文本视图采用的是正文字体样式 左边的字体大小是 iPad 标准的 17 号字体 右边的则是 Mac 标准的 13 号字体 众所周知 这些字体在视觉上大小匹配 但是由于为 Mac 优化的视图 采用的是真正的 13 号字体 因此它看起来更锐利 请记住多余的行数 是因为边距的增宽 和文本渲染或者字体大小无关 如果你想知道具体数值 我已经为这张幻灯片批注了布局数值 好让你可以说服自己 这一切的变化都是合理的
把这些集合到一起 你就能看到当你选择为 Mac 优化时 你就必须进行一些工作 甚至是重构一些简单的布局
让我们来整理一下这个 Mac 布局
这款 app 在 Mac 上真的太自然了 我们刚才探讨的并不是 将经验法则应用到布局上 而是了解哪些元素会被改变 以及如何被改变 成果就是这款看起来完美无比的 app
在我们离开这个视图之前 请看一下左边的“Get Cooking”按钮 它的色调是 systemTeal 而右边的 完全没有把它的色调带到 Mac 上 上色按钮并不是 Mac 的标准 用户也不习惯这些颜色 所以为了最理想的 Mac 显示 那个色调被摒弃了 事实上 还有很大一部分控件定制 在为 Mac 优化时都是不可用的 要么是因为那种定制是 iPad 的标准而非 Mac 的 要么是因为 它们不被 Mac 视觉显示所支持 (用户界面控件手势识别器) 为 UIButton 增添 手势识别器来侦测长按 并且运行多种操作是很普遍的一件事 如果你使用的是 采用了 Mac 外观的 UIButtons 你用户界面按钮上的 手势识别器就不会被调用 (UIControls 上的手势识别器) 你可以用几种方式来处理这件事 首先 确保你不是把 iPad 互动带到了 Mac 上 如果 app 菜单栏上有个菜单会更好处理 或者用 UIButton 菜单属性 给 UIButton 增添菜单 就像地图一样 在优化 app 时 我们建议你在事件处理阶段时 审查定制控件 这包括控件上的手势识别器 比如 UIButtons 以及覆写 UIControl 事件跟踪方式 比如 beginTrackingTouchWithEvent 如果手势识别器是必不可少的 请记住自定义按钮类型 不会照着 Mac 外观渲染 而是会和 iPad 一样 保持相同的事件跟踪 这样你的手势识别器就不会失去作用 看看右边这个自定义滑块 (UIControls 上的视觉定制) 我真的彻底改变了它 它有着白色的 minimumTrackTint 颜色 和蓝色的 maximumTrackTintColor 也把环形拇指替换成了一架飞机
在为 Mac 优化时 你可能会有些混乱 在 Mac 惯用法上 这些定制是不可用的 一个有效的思维模式 是当你使用系统控件时 你的外观定制是有限的 因为标准 Mac 外观会覆盖 现有的 UIKit API
我现在会把舞台交给 Jake 他会讲述如何更改缩放 Catalyst app 来为 Mac 优化
谢谢 Nick 大家好 我是 Jake 我是 Xcode 团队的工程师 今天 我将给你们展示 我们正在开发的一款 简单 Mac Catalyst 食谱 app 我们直接看演示版本吧
在我们开始为 Mac 优化之前 我们先大致看一下这款 app 吧 左边有侧边栏 方便你访问 app 里的通用区域 比如“全部食谱”和“收藏列表” 接下来 我们有食谱列表 最后则是食谱自己
我们可以通过点按 “新食谱”按钮来添加新食谱
在这里 我们可以添加食谱细节和照片 这款 app 在 Mac 上看起来已经很好了 但也有一些东西看起来格格不入 比如极具 iOS 风格的按钮和导航栏 让我们切换到 Xcode 然后开始吧
这是对象的常规设置 你会发现在“Mac”复选框旁边 有个新的弹出按钮
在默认情况下 它会选择 “缩放界面以匹配 iPad”的选项 随着 Xcode 12 和 macOS 10.16 的推出 新增的选项是“为 Mac 优化界面” 让我们选这个吧
这样做会告诉 UIKit 我们想要 app 以 Mac 惯用法运行 让它具有类似于 Mac 的外观和感觉 让我们构建和运行看看什么会被改变吧
我设置好了这个以便可以同时看到 “缩放界面以匹配 iPad” 和“为 Mac 优化界面”这两种版本 你立刻就可以看到窗口变大了 但不仅如此 窗口里的内容也变大了 这是因为它们不再被缩放了
接下来 我想说的是“计时器”按钮 只是点按“为 Mac 优化界面”而已 这个按钮就已经自动变得 更具有 Mac 风格
还有其他的变化 按钮不只是因为不再被缩放而变大了 它也使用了另一种度量 这是什么意思呢?
我的意思是有些东西的大小改变了 比方说 字体变大了 文本和按钮边缘之间的边距也变大了
因为这些改变 按钮的边框变大了 在更改并检查你的布局时 你应该把这些度量的变化谨记于心
好了 我们接着仔细看看这个食谱列表 我们知道左边的食谱图片 和右边的“收藏列表”图标 都变大了一点 因为它们不再被缩放了 但如果你仔细看 你会发现字体大小并没有改变 那是因为我们使用的是动态字体样式 它会自动调整成最佳易读度 字体大小看起来非常好 但我觉得要是食谱图片 和“收藏列表”图标更小一点 就像之前的那样 那就更好了 我们回到 Xcode 让这个列表看起来更好吧
这是管理食谱列表的视图控制器 如果你看一下这个创建布局的代码 你就会发现我们使用的是 100 分的绝对高度
因为我使用了自动布局限制 如果这个高度降低 它也会导致食谱图片变小
我之前研究过这个 发现 80 分非常好
因此我会更换这一行代码 来看看惯用法的变化 如果是 Mac 我们就把高度设置为 80 否则就保持在 100 现在我们看看收藏列表图标
我在资产目录里指定了一张图片 我只有“通用”版本 因此它会在任何运行的 地方上使用这个版本 在这种情况下 iPhone、iPad 和 Mac 都使用了同一版本 正如我们所看到的那样 当在 Mac 上运行时 这个版本的图片 在文本旁边显得太大了
既然现在它是在 Mac 惯用法上运行了 它就会有权限访问 资产目录里的 Mac 资产 这可以让我们专门设置图片 然后使用被手工构建至正确大小的图片
要想启动 Mac 资产 我们只需要 在 inspector 里的“设备”下 点击“Mac”的复选框 现在我们可以拖动 Mac 版本的图标了
就是这样 让我们构建 并运行看看刚才的列表 现在你可以看到我们的单元格高度变低了 这也让食谱图片变小了 我们右边的“收藏列表”图标大小 也变得更合适了 因为它使用的是专门定制的 Mac 资产
让我们接着看“新食谱”画面
我们可以看到 Mac 风格的“选择图片”按钮 看起来真的很不错 但这里还有一些东西 看起来和 Mac 格格不入 就像最上面的导航栏 导航视图控制器和导航栏 在过渡到子视图控制器时特别有用 这样你就能在小屏幕里显示更多数据 这样的互动却在 Mac 上显得格格不入 把“最常使用”的按钮 放到导航栏上也是很常见的 在 Mac 里 上方显示窗口标题的工具栏 是对于这类用户界面更好的选择 因此 “保存”和“取消”的按钮应该被移动 要么移上去工具栏 要么移下去视图 而导航栏应该被彻底移除 因为“保存”和“取消”的按钮 是按照进入视图的数据所行动的 而这两个按钮最终都会关闭窗口 因此把它们移到视图下方 会更具有 Mac 风格 我也看到了一个错误 现在我们是在 Mac 惯用法上运行 可是“标题”标签却比另外两个标签还要大 我们回到 Xcode 修复一下这个错误吧
这是在“新食谱”窗口里使用的 RecipeEditorViewController 我想做的是 在 Mac 上运行时 隐藏导航栏
要想修复这个 我们就只需要检查惯用法 如果是 Mac 我们就隐藏导航栏 很简单 接下来 我们看 storyboard
既然现在我们的项目支持 Mac 惯用法了 你会看到下方设备栏这里 有了新的惯用法选择器
当我切换成“Mac”时 注意看上方的视图
你会注意到 canvas 改变了 控件看起来更具有 Mac 风格 并且它们使用的是 Mac 的标准间距和度量
我要修复的第一样东西是“标题”标签 在运行时 它比其他两个标签还要大
如果我们在 inspector 里 查看“字体”属性 你会看到它被设置使用 17 号系统字体 就像 Nick 之前提到的那样 它会被缩放至 13 号 但现在我们选择了 “为 Mac 优化界面”的选项 它又被渲染成未缩放的 17 号字体 所以它才会变大 但为什么其他两个没有变化呢?
如果我们选择这两个的其中一个标签来看 我们可以看到 它被设置使用“正文”字体样式 就和我之前提到的一样 这些字体样式是动态的 它们会被调整至适合其渲染平台的大小 我们强力建议你使用这些字体样式 我们去更新一下“标题”标签 让它也使用“正文”字体样式吧
接下来 我们来看“保存”和“取消”按钮 请记住 我们已经在代码里隐藏了导航栏 所以即使它出现在了这里 可在运行时 它是不会出现的 让我们在视图下方添加 新的“保存”和“取消”按钮 并设置成只在 Mac 惯用法上出现吧
首先 我会调整下方文本视图 来为新按钮腾出空间
当我这样做时 Interface Builder 会提醒我 canvas 里的边框大小不再匹配 自动布局所计算出的边框大小
我先保持这样以便有空间放置我们的按钮
现在 我会去拖动多层视图
把它放到右下方的位置上
我会调整间距 让它使用系统标准间距
然后把它的“分布模式”设置成“平均填满” 这样按钮的大小就会是相同的
接下来 我会拖出按钮并更改它们的标题
(取消 - 保存) 我只要在 Mac 惯用法上运行 app 时 才让这些按钮出现 所以我选择多层视图
然后使用属性 inspector 下方的 “已安装”属性
这些属性会指定是否要在运行时 把视图加入到视图层级 勾选“已安装”选项后 多层视图就会被添加至视图层级 但我们可以通过点击左下方的“加”键 来添加特质变化
(根据以下选项引入变化 宽度 - 高度 - 色域 - 惯用法) 这可以让我们根据这些特质来改变属性 在新的 Xcode 12 里 我们新增了 根据 Mac 惯用法改变特质的选项 我会根据这些特质增加一个版本变化
我们在这个新 Mac 惯用法变化上 保持勾选“已安装”选项 不过在“默认”场景里 我们取消勾选
现在这些视图只会 在 Mac 惯用法上运行时安装
接下来 我们要确保 文本视图不会覆盖住新按钮
要想做到这点 我会控制拖动它到多层视图里
(垂直间距 垂直基线标准间距) 当我按住键盘上的“Option”键时 最上方的选项 从“垂直间距”变成了“垂直标准间距” 我就选这个
我们成功加入了这个限制 但有些限制却变成了红色 这意味着我们有着不可满足的限制
如果我们看下方的红色限制 你会看到文本视图和父视图之间 依然存有 20 分的间距 这和我们新增的限制相悖
我们不能只是删除旧限制 因为这样的话 当在 iPad 上运行时 我们的新按钮就不会被安装 我们需要用其他方法 来指定文本视图的高度 有几种方法可以解决这个问题 但我认为我会选择改变限制的“优先级”
它现在的级别是“必要” 那我们改成“高度优先”吧 这样一来 当它无法被满足时 它就会被忽略
现在所有限制都变回橙色了 我们要做的是点击右下方的 “更新边框”按钮
现在限制可以在 Mac 和 iPad 惯用法上运行了 我们可以使用设备栏里的 惯用法选择器来验证这点
(新食谱) 现在当我打开“新食谱”窗口时 你可以看到“标题”标签的字体大小刚刚好 导航栏也被隐藏起来 而且下方还有“保存”和“取消”的按钮 这感觉更像是 Mac 的风格了 现在我们回到幻灯片来回顾一下重点 (回顾重点) 通过选择“为 Mac 优化界面”的选项 你就是在告诉 UIKit 你想要在 Mac 惯用法上运行你的 app 正如我们所看到的那样 这会让你的 app 看起来更有 Mac 的感觉 但这也意味着布局会被改变 因为它会应用 Mac 标准间距和控件度量 这意味着控件、图片视图 和字体大小都会有所改变 因此当做出更改之后 你真的需要审查一下布局 你可以检查一下 日志里“不可满足的限制”警告 这可以帮助你找到 因控件大小更改之后导致的问题 我们也在资产目录里提供了 Mac 资产 以帮助解决这些问题
需要记住的是 当系统无法找到 Mac 资产时 它会在使用“通用”资产前 回归“缩放至 Mac”和“iPad”的资产 正如演示版本所示 这些资产的大小不会很理想 所以请确保你在适当的位置 审查并提供 Mac 版本的图片 最后我们看了 Mac 和 iPad 之间的模型差别 比如导航栏和按钮布局 在设计你的 Mac Catalyst app 时 你应该谨记这些差别
我强烈建议你阅读我们的 Human Interface Guidelines 以了解更多关于 设计极具 Mac 风格 app 的信息
虽然你可以反部署已经为 Mac 优化了的 Mac Catalyst app 但我们还是希望你能记住几件事 (为 Mac 优化界面) 在 Catalina 上运行时 这些 app 不会获得 类似于 Mac 的外观和感觉 这也意味着它同时支持 iPad 和 Mac 惯用法 我们发现在反部署时 绝大多数的工作就是 确保你的布局能够处理 两种惯用法上的度量 你也需要持续地看守不受 Mac 支持的 框架和 API 使用
你可以考虑 在 traitCollection 上检查惯用法
或者使用条件编译块来解决这类问题 请记住 无论在哪种惯用法上运行 条件编译块始终都会保持为“真”
在某些情况下 你可以同时使用两种方法
好了 我就讲到这里为止 我会把舞台交还给 Nick 他会和你讲解为 Mac 优化的 SwiftUI 交给你了 Nick 谢谢你的演示 Jake 现在那款食谱 app 在 Mac 上看起来非常好
你可能知道对于缩放匹配 iPad 的 Catalyst app 来说 SwiftUI 是可用的 (为 Mac 优化的 SwiftUI) 对于那些为 Mac 优化的 app 来说 SwiftUI 也是可用的 在为 Mac 优化的 Catalyst 里 SwiftUI 和 UIKit 采取了类似的方法 但你能免费获取更多东西 在为 Mac 优化时 它的声明式语法和布局 可以为视图做出更多事
如果你之前使用过 SwiftUI 你就知道它是如何 在情境感知的基础上调整你的视图 比方说 改变在表格或菜单里的按钮样式 SwiftUI 也会知道 视图是否正在 Mac 惯用法上运行 就像你的控件会在 iPadOS 视图里 改变外观一样 当你为 Mac 优化时 这些视图的外观也会跟着改变
你还是需要进行一些布局工作 来为 Mac 优化 SwiftUI 的界面 基本上 你的 SwiftUI 布局会根据 你用于构成视图的容器而移动 一如既往地 你可以创建 更灵活且可重复使用的布局 让你的视图保持模块化 并且善用容器
让我们快速地看一下 SwiftUI 的控件 以及它们的外观会如何 从“缩放以匹配 iPad”变成“为 Mac 优化” 今年 GroupBox 登上了 iOS 和 iPadOS GroupBox 被用于分层结构化内容 和自动接收正确的语义色调 来作为内容的背景 (高级信息 一些细节) 我在嵌套在另一个 GroupBox 里的 VStack 里使用了 GroupBox 视图以不同的背景颜色进行了自动渲染
当你为 Mac 优化时 你的 SwiftUI GroupBox 会呈现为 Mac GroupBox 也会伴随一些自动边距和布局调整 (已完成?) 在代码里 我设置了切换开关 它使用完成的变量作为它的 Bool 约束力 你可以看到在 iPadOS 上 SwiftUI 的默认切换开关样式是 滑动开关
我们已经看过 Mac Catalyst 和 UIKit 会让 Mac 复选框变得可用 SwiftUI 也是如此 在为 Mac 优化的 Catalyst 里 默认的切换开关样式是一个复选框 如果你不满意默认样式 你可以用 ToggleStyle 结构 和 ToggleStyle 修饰符来指定样式
和 UIKit 一样 iOS 系统按钮会适应 Mac 原生外观 你甚至可以放置 SF Symbols (点我!) 这两个按钮看起来非常好看 我不知道你是怎么想的 但是其中一个似乎看起来更可以点击
DatePicker 又有些不同 在右边 你可以在 Catalyst 的 两种惯用法上看到 DatePicker DefaultDatePickerStyle 在 Catalyst 中拥有相同的渲染 请记住 你当然也有 其他可用的样式选择 DefaultDatePickerStyle 在两种惯用法上都很简洁 (尺寸:小 - 中 - 大) 这个内容视图设置了一个挑选器 让你可以从小、中、大里进行选择 它是通过属性 sizeIndex 设置约束力的 你也可以通过字符串值或枚举来约束它
当你为 Mac 优化时 Mac 挑选器会变得可用 这是 Mac 用户非常熟悉的控件 也比滚动挑选器更能提供 Mac 风格的体验 请记住 segmentedControlStyle 在两个平台上都可用
最后还有一个控件要说 你知道你可以使用系统按钮 但你也不想牺牲 SwiftUI 按钮上的 可定制性 (太棒了!) 如果你用的不是系统按钮外观 那你就是在使用定制外观 希望你用的是我这里的 SwiftUI ButtonStyle API 来让你的按钮样式变得 更灵活且可重复使用
SwiftUI 和 Catalyst 知道 它们不应该将定制按钮 渲染成 Mac 的外观 这样你的定制按钮才会保持原样
我花了一些时间编写 一个完全定制的 怀旧 NinetiesButtonStyle 你可以看到我在 ContentView 的正文里 把它作为视图修饰符应用到按钮上 多亏了 Catalyst 我可以 在 iOS 和 Mac 上拥有这个按钮样式 而无需编写任何额外代码
和你们其中一些人一样 我一直在对 SwiftUI 进行试验 你可能知道 SwiftUI 可以在 UIKit app 内无缝运行 要在 SwiftUI 预览的 canvas 内建立内容视图 然后通过 UIHostingController 把它放置到 UIKit 代码里 是很容易的一件事
我认为刚才 Jake 所展示的 食谱 app 的仅文本视图 需要进行一些修饰
所以我用 SwiftUI 创建了 这个互动式食谱视图 它可以让用户在准备食谱时 勾选他们的进度
你可以通过上方的挑选器 选择你的测量单位
每个指示都放置在一个 GroupBox 里
并且可以通过切换开关来标记完成
我用了定制蓝色计时器视图 和定制 SwiftUI 按钮 来控制计时器 让我们来看看这个新的 iPad 功能 如何转移到 为 Mac 优化的 Catalyst app 中吧 这是只有文本指导视图的 app 这是采用了 iPad SwiftUI 视图的 app 完全没有更改任何代码 我真的很喜欢这些改变
挑选器采用了默认的 Mac 样式 iOS 风格的 GroupBox 现在变成了 Mac 风格 使用 GroupBox 也让整款 app 看起来更像是 Mac 风格的布局 我也不必亲自动手操作
Mac GroupBox 采用了 Mac 度量 所以它在视图里会看起来更整齐 GroupBox 的标签也移到了框外 这也影响了完整指示的外观
滑动开关现在都成了复选框 而那些复选框的标签都位于框的后缘 这些标签也可以点击以触发复选框 这和滑动开关标签的反应不一样
我的定制视图也没有被改变 并且缩放正确
我很高兴能够这么轻易地 把 SwiftUI 带入这款 app 里 然后在 Mac 和 iPad 上 让遵循食谱变得更容易 并且可以让用户与之互动 让我们回顾一下今天所学到的事物吧 你看到在优化 Catalyst 界面时 会改变的东西
我们也探讨了拥有哪种特质的 app 会是为 Mac 优化的理想对象
Jake 向你们展示了如何在 Xcode 里 使用 Interface Builder 的 特质变化来优化 app 并且如何在代码里检查惯用法
最后 我们看到 SwiftUI 是如何为 Mac 优化的 以及将新 SwiftUI 视图 集成到 UIKit app 里 是多么容易的一件事
谢谢观看 好好享受 WWDC 2020 吧
-
-
22:04 - Hide Navigation Bar in Mac Idiom
if traitCollection.userInterfaceIdiom == .mac { navigationController?.setNavigationBarHidden(true, animated: false) }
-
29:33 - Idiom vs conditional compilation block
// Idiom vs conditional compilation block if traitCollection.userInterfaceIdiom == .mac { // "Optimize Interface for Mac" specific code } #if targetEnvironment(macCatalyst) // Mac Catalyst specific code #endif if traitCollection.userInterfaceIdiom == .mac { // "Optimize Interface for Mac" specific code } else if traitCollection.userInterfaceIdiom == .pad { #if targetEnvironment(macCatalyst) // Mac Catalyst specific code #else // iPad specific code #endif }
-
31:26 - SwiftUI GroupBox
// Nested GroupBoxes struct ContentView: View { var body: some View { GroupBox { VStack { Text("High level information") GroupBox { Text("Some elaborate details") } } } } }
-
32:00 - SwiftUI Toggle
// DefaultToggleStyle struct ContentView: View { @State var completed: Bool = false var body: some View { Toggle("Complete?", isOn: $completed) } }
-
32:35 - SwiftUI Button
// System Button with SF Symbol struct ContentView: View { var body: some View { Button(action: { }, label: { HStack { Image(systemName: "rays") Text("Click Me!") } }) } }
-
32:56 - SwiftUI DatePicker
// DefaultDatePickerStyle struct ContentView: View { @State var dueDate = Date() var body: some View { DatePicker("Due:", selection: $dueDate) } }
-
33:14 - SwiftUI Picker
// DefaultPickerStyle struct ContentView: View { @State var sizeIndex = 2 var body: some View { Picker("Size:", selection: $sizeIndex) { Text("Small").tag(1) Text("Medium").tag(2) Text("Large").tag(3) } } }
-
33:55 - SwiftUI Nineties Style Button
// Custom gradient button struct CustomNinetiesButtonStyle: ButtonStyle { var angle: Angle = .degrees(54.95) func gradient(shifted: Bool) -> AngularGradient { let lightTeal = Color(#colorLiteral(red: 0.2785285413, green: 0.9299042821, blue: 0.9448828101, alpha: 1)) let yellow = Color(#colorLiteral(red: 0.9300076365, green: 0.8226149678, blue: 0.59575665, alpha: 1)) let pink = Color(#colorLiteral(red: 0.9437599778, green: 0.3392140865, blue: 0.8994731307, alpha: 1)) let purple = Color(#colorLiteral(red: 0.5234025717, green: 0.3247769475, blue: 0.9921132922, alpha: 1)) let softBlue = Color(#colorLiteral(red: 0.137432307, green: 0.5998355746, blue: 0.9898411632, alpha: 1)) let gradient = Gradient(stops: [.init(color:lightTeal, location: 0.2), .init(color: softBlue, location: 0.4), .init(color: purple, location: 0.6), .init(color: pink, location: 0.8), .init(color: yellow, location: 1.0)]) return AngularGradient(gradient: gradient, center: .init(x: 0.25, y: 0.55), angle: shifted ? angle : .zero) } func makeBody(configuration: ButtonStyleConfiguration) -> some View { let background = NinetiesBackground(isPressed: configuration.isPressed, pressedGradient: gradient(shifted: false), unpressedGradient: gradient(shifted: true)) return configuration.label .foregroundColor(configuration.isPressed ? Color.pink : Color.white) .modifier(background) } struct NinetiesBackground: ViewModifier { let isPressed: Bool let pressedGradient: AngularGradient let unpressedGradient: AngularGradient func body(content: Content) -> some View { let foreground = content .padding(.horizontal, 24) .padding(.vertical, 14) .foregroundColor(.white) return foreground .background(Capsule().fill(isPressed ? pressedGradient : unpressedGradient)) } } } struct ContentView: View { var body: some View { Button("Awesome", action: {}) .buttonStyle(CustomNinetiesButtonStyle()) } }
-
-
正在查找特定内容?在上方输入一个主题,就能直接跳转到相应的精彩内容。
提交你查询的内容时出现错误。请检查互联网连接,然后再试一次。