大多数浏览器和
Developer App 均支持流媒体播放。
-
Mac Catalyst 中的新功能
探究 Mac Catalyst 的最新更新,了解让您的 app 在 macOS 上运行时感受更加自然的方法。了解各种新和增强的 UIKit API,它们使您可以自定义自己的 Mac Catalyst app,以利用 macOS 独有的功能。为了充分了解本节内容,我们建议您要基本熟悉 Mac Catalyst。查看 WWDC19 的“介绍 Mac 的 iPad App”,以熟悉相关知识。关于优化 Mac Catalyst app 的更多信息,请观看 WWDC20 的“优化 Mac Catalyst app 的界面”。
资源
- Bring an iPad App to the Mac with Mac Catalyst
- Building and improving your app with Mac Catalyst
- Human Interface Guidelines: Mac Catalyst
- Mac Catalyst
相关视频
WWDC21
- 出色 Mac Catalyst app 的质量
- 带有 M1 的 Mac 上的出色 iPad 和 iPhone app 的质量
- 认识 macOS 快捷指令
- 认识 UIKit 按钮系统
- UIKit 中的新功能
WWDC20
WWDC19
-
下载
欢迎收看Mac Catalyst 最新消息专题讲座 我是杰森比福 待会我的同事 尼克泰斯勒会加入我们 Mac Catalyst是种技术 让你能将 你的既有iOS应用程序带到macOS上 让应用程序能善加利用 Mac的较大显示器 集成键盘 鼠标或触控板 数千位开发者已经用Mac Catalyst 将他们的iOS应用程序带到macOS上 而且结果十分惊艳 让我介绍几个 最近获得Apple 设计大奖的 Mac Catalyst应用程序
Shapr3D是工业级CAD工具 Mac和iPad上都可用 虽然iPad版本设计为使用多点触控 和Pencil的体验 新的Mac版本 用Mac Catalyst打造 全面善用鼠标 和键盘支持 就像台式机用户预期的
Instapaper是非常受欢迎的应用程序 用来脱机阅读文章 有赖于Mac Catalyst 它也在Mac上初登场 Notability是极佳的笔记应用程序 这个新版本取代了既有的Mac版本 让用户可使用iPad上的所有炫酷功能 但又经过优化 加以利用 Mac的大屏幕 键盘和更快的速度 它甚至支持用Apple Pencil使用随航 我们会先介绍新API概览 然后介绍我们在macOS Monterey中 加强的API 最后 尼克会在演示应用程序中 采用这些API 借此展示新API的运作 我们先进行 macOS Monterey中的新API概览
之前在macOS Big Sur 我们新增了 用showsmenuAsPrimaryAction属性 将菜单附加到按钮的功能 这让你能创建下拉菜单
现在 在macOS Monterey中 我们新增了 弹出按钮的支持方式 是用一个新属性 叫做 changesSelectionAsPrimaryAction 它让按钮的标题 跟踪菜单中的选择 有了这两个属性 现在共有四种方式 可以配置按钮
两个属性都设定为false时 我们会获得一个标准的按钮 若设定成单击并按住按钮时 会显示菜单 它还是可以显示菜单 创建那个按钮的代码如下方所示
如果只有 changesSelectionAsPrimaryAction 设置为true 你会获得一个切换按钮 可以点击以开启或关闭 这可以用来指示应用程序中的状态
如果只有showsmenuAsPrimaryAction 设置为true 你会获得下拉菜单 如果两个属性都设置为true 你会获得新的弹出按钮 对于主要操作 配置为显示菜单的按钮 执行主要交互时 这在macOS中是左键单击 按钮会立刻显示菜单 不过 对于主要操作 不是配置成显示菜单的按钮 它的行为取决于应用程序的惯用法 在iPad 惯用法里 菜单是用次要交互触发 在macOS中是右键单击 但如果应用程序采用Mac 惯用法 菜单是由长按按钮触发
想了解更多按钮的新功能 可观看《Meet the UIKit button system》视频 我们现在让ToolTip也可用于 Mac Catalyst应用程序 ToolTip是一个浮动的小窗口 会在光标悬停在 应用程序的视图上时出现 它可用来提供关于内容的上下文 或额外细节 要将ToolTip添加至任何UIView 请用UIToolTipInteraction 你只需要用文字字符串创建一个 UIToolTipInteraction 然后将那个交互添加到你的视图
不过其中一个ToolTip的常见用途 是阐明UI控制的行为 这个模式很常见 因此我们添加了一个方便的属性 到UIControl 让你能快速设定ToolTip
iOS应用程序各处都使用文本标签 显示不能编辑的字符串 当标签的内容太长塞不下 标签必须截断文字 现在 在macOS Monterey中 我们添加了 扩展ToolTip的支持 让标签的完整内容 能够显示 这个范例UI中 标题标签的内容太长 放不下 通过启用扩展ToolTip 我们将鼠标悬停在标签上时 它会显示完整标题 你可以用UILabel上的新属性启用它 属性叫做 showsExpansionTextWhenTruncated
我们推出了新的info.plist键 叫做UIApplicationSupports PrintCommand 你可以将它添加到你的info.plist 如果设置为true 我们会在启动时 自动添加“打印” 和“导出为PDF”菜单项目 到你的Mac Catalyst应用程序 这个plist键也可以添加到 iOS应用程序 在iPadOS上的快捷方式提示中 只会出现“打印”选项 而当iOS应用程序在M1芯片Mac上运行 则也会显示“打印” 和“导出为PDF”菜单项目
虽然这个plist键告诉操作系统 你想要“打印” 和“导出为PDF”菜单项目 这只是故事的一半 你还是需要在代码中明确实施 打印支持 要做到这点 有个新的UIResponder操作 叫printContent 你可以在任何UIResponder上实施它 它会用响应链搜索 寻找适当的打印目标 当你实施的printContent被调用 只需设定并使用 UIPrintInteractionController 就跟平常一样 我们现在支持添加副标题到窗口 这可以用来提供 你的应用程序状态的辅助信息 这可以用UIScene上 叫做副标题的新属性 进行设定
macOS Monterey中 Shortcuts应用程序 现在可以在Mac上使用 如果你的应用程序在iOS上采用 自定义意图 它们现在也可以在 Mac上的Siri和Shortcuts使用 应用程序内意图处理和Intents扩展 都可使用 所以如果你先前在 Mac Catalyst应用程序中禁用了 构建你的Intents扩展 你现在可以重新启用它 想了解更多 请观看 《Meet Shortcuts for macOS》视频 以上就是macOS Monterey中 新的API的概览 我们接着进行macOS Monterey中 增强的API的概览 如果你写过符合你的应用程序主题的 自定义按钮或滑块 你可以 在采用Mac 惯用法时 在Mac Catalyst中保留这些 方法是选择禁用本地控件 但你需要自己缩小到77% 我们并不建议对整个应用程序 采取这个做法 只建议用于 对应用程序体验有关键影响的 特定控件 新的按钮系统也可以用来 以更加跨平台的方式 创建符合你的应用程序主题的按钮 若想了解更多 请看《Meet the UIKit button system》视频 UIBehavioralStyle 是让这件事成真的新枚举 UIButton和UISlider各有两个新属性 preferredBehavioralStyle 是可读可写的 而behavioralStyle是这个属性的 只读解析值 在iOS上 behavioralStyle的解析值 不会变 所以很容易把这行代码明确设定成 .pad 或.mac 以处理Mac Catalyst案例 而不会影响到你的iOS应用程序
对于基于文档的应用程序 有个新plist属性 叫UIApplicationSupports TabbedSceneCollection 可用来选择禁用窗口选项卡 当设置为false 窗口选项卡会被禁用 默认的选项卡相关菜单项目 不会被添加 我们现在支持UIPointerLockState 用来隐藏并锁定光标 让它不会移动到Mac窗口外面 这在玩游戏时很有帮助 让用户不会不小心 点到游戏以外的地方 让别的应用程序跑到前台 它会在你切换应用程序或窗口时 暂时解除锁定窗口 并在你单击窗口时重新锁定
我们也支持UIPointerShape 它可以用来 取得iBeam光标 横轴纵轴都可以 当你在macOS上用这个API 你会获得 典型的NSCursor光标形状之一
最后 我们支持可隐藏的指针风格 让你能在必要时 隐藏应用程序中的光标 我们谈了几个全新的 和增强的UIKit API 现在我将画面交给尼克 他会介绍 我们会怎么用这些新的、增强的API 让我们的Mac Catalyst应用程序 在Mac上感觉更自然 谢谢杰森 那些新功能很有趣 我已经有机会试用过了 我会详细演示一个 由Catalyst团队开发的应用程序 原来Catalyst团队晚上会 摇身成为旅游作家 所以我们开发了Trip Planner iPhone和iPad皆可用 这是Trip Planner 在M1芯片的13吋MacBook Pro上运行
为了让我们的应用程序 能接触到最多用户 我们选择让应用程序 能在M1芯片Mac上运行 让我介绍一下应用程序的功能 Trip Planner这个应用程序 会列出全球各地 能造访的好地方 并且让你兑换 各种常旅客计划的积分 它的UI基于一个三列的 Split-view拆分视图控制器 利用边栏 我可以导航 我最爱的国家的住宿、餐厅和景点 或是选择奖励计划 检视并兑换积分 当我选择边栏的某样东西 如果它是一个内容分类 Trip Planner会在 拆分视图控制器的附属列 显示它的内容 附属视图控制器 写入日本、西班牙、巴西 和坦桑尼亚的 景点、餐厅和住宿 如果我是选择边栏中的叶项 或是附属视图控制器中的项目 次要视图控制器会显示 那个项目的细节 我要选我在日本最爱的咖啡厅之一 超级抹茶 细节视图控制器包含 一些关于咖啡厅的文字 协助我规划旅程的按钮 还有显示超级抹茶位置的地图视图 在Trip Planner的例子里 一切在M1芯片Mac上都是开箱即用 不过 因为它是iOS应用程序 要让它在Mac上感觉自然 我们能做的有限 若要更进一步 我们需要勾选这个复选框 让它成为Mac Catalyst应用程序 更准确地说 是这个复选框 可以放大吗? 通过勾选这个复选框 增强你的应用程序 这是为Mac Catalyst优化后的 Trip Planner 我们添加了Mac特定的功能 像是用双击手势识别器打开新窗口 自定义标题栏外观 我们还全面升级 通过为Mac优化 让我们有本地控件和清晰文字 请看《Qualities of a great Mac Catalyst app》 和《Optimize the interface of your Mac Catalyst app》 以了解那个过程的完整介绍 在Trip Planner的最终版本中 我采用了杰森刚刚向你介绍的新API 我会讲解每个的采用 我非常建议你下载范例项目 我在这里展示的所有片段代码 都直接来自那个项目的代码 这是为macOS Monterey 全面更新后的应用程序 我再次选择超级抹茶 你可能注意到UI当中有些颜色 是macOS风格按钮的形式 有背景色 但晚点再说那个 我想先谈谈场景副标题 我用新的UIScene副标题属性 做两件事 第一 我用副标题 用在Mac上更自然的方式 显示细节视图控制器的标题 比起导航项目的标题 窗口副标题比较像Mac会出现的 第二 我要更改副标题 以在用户导航应用程序时 提供一些有帮助的上下文 在Trip Planner中 我选了 日本和坦桑尼亚
所以窗口的副标题是“国家” 如果我选择这个小露营地 在附属视图控制器这里 副标题会变更 让我知道 我选择了乞力马扎罗山豪华露营 如果我选择整个奖励计划部分… 副标题会再次变更 成为“国家&奖励计划” 让用户有更多上下文 想想副标题也可以如何帮助 你的Catalyst应用程序 添加上下文 也请注意 每个UITitlebar toolbarStyle 副标题显示的方式不同 试验看看哪个最适合你的应用程序
要设定副标题 先从字符串值入手 取得场景的引用 然后设定副标题属性 你可以在场景连接时设定 或是之后通过 访问视图窗口的场景进行设定 接着 我会示范 我如何在Trip Planner中 采用ToolTips macOS一个很常见的交互模式 是暂时将鼠标悬停在某个东西上 取得那个东西的更多细节 这个技巧是确立已久的方法 能用来教育用户 而不会让应用程序的UI过于拥挤 ToolTips也可以显示 被截断标签的完整文字
这里我选择了巴西的伊圭苏瀑布 将鼠标悬停在图像上一下子 就会出现ToolTip! “阴天里 苍翠繁茂的森林 围绕着滔滔的伊圭苏瀑布” 真是不错的描述 我们用了新的 UIToolTipInteraction API 将这个功能添加到所有的图像 它让应用程序感觉更符合Mac了
我也用了UILabel API 让因为国际化货币格式被截断的标签 能够扩展 让它们比预期更长一点
就像杰森刚刚介绍过的 有不同形式的ToolTip API 对主图来说 用UIToolTipInteraction API 而没UITooltipInteractionDelegate 适合我的需求 因为我想要 将固定文字 作为ToolTip添加到任意视图
对于货币文字 我用UILabel上的 showsExpansionText WhenTruncate属性 如果我想附加一个ToolTip 到任意控件 那我应该用的API 是UIControl上的ToolTip属性
我会接着介绍 macOS Monterey上的Catalyst 可用的多种新按钮 建议你观看 《Meet the UIKit button system》视频 那支视频介绍新的iOS 15 UIButtonConfiguration API 新的UIButtonConfiguration属性中 很多都在 为Mac优化的Catalyst应用程序中 有自己的表现形式 更好的是 就像在iOS上 UIButton上定义的属性 可以和UIButtonConfiguration上的 进行混搭 我会让你看一系列按钮 然后我会展示如何在代码中 配置各个按钮
我会选一个新的目标 展示新的按钮背景色 选科帕卡巴纳如何? 这里的按钮是用着色配置 所以它们会接收环境的色调 自动取得匹配图像 而且背景模糊效果良好的颜色 现在我要导航到奖励细节视图控制器 我会从边栏打开我的计划…
并选择钻石达布隆奖励计划 这个视图的目的是让我能够 兑换我通过旅游获得的积分 并选择兑换的项目
我可以用滑块选择要兑换多少积分 我可以用这个切换按钮激活 积分乘法器 甚至用附加菜单选择倍数 感觉选择最大的数字 作为积分倍数很合理 所以我要选六 不知道有什么圈套 我可以用这些较大的按钮切换 以选择兑换的类型
我可以选择兑换 兑现 或捐出我的积分 当我终于准备好提交 我可以单击右下角的提交按钮 或单击重置按钮以重新开始 这里有好几个新按钮 我会讲解每一个要如何配置
我会先讲我们都很熟悉的 下压按钮 在细节视图控制器中 这原本是提交按钮 这只是系统类型的UIButton 在Big Sur中首次推出 角色设定为主要
接着 我有积分倍数菜单按钮 这个按钮能在主要单击时切换状态 并在长按时显示菜单 我知道我终究会想要自定义背景色 所以我会使用着色配置 并将标题设定为“积分倍数”
要获得切换的行为 我会用杰森告诉我们的新布尔属性 changesSelectionAsPrimaryAction 设定为true 现在按钮的背景色 会在应用程序的色调 和标准无色外观间切换
我也会附加一个菜单 配置好倍数值和一些UIAction
最后 在菜单的操作处理器中 如果用户选择最大积分倍数 我们会有条件地将切换按钮的背景色 变更为系统红
现在要配置重置按钮
我可以用普通配置 获得无框架的外观 这个配置看起来十分类似 iPadOS的外观
我会将按钮的角色设定为破坏性 这会告诉系统进行一些活动处理 预防不小心触发操作 并将它的tintColor设定成systemRed 这个按钮讲完了 至于视图左下角的弹出按钮 能让我选择兑换、兑现和捐出的那个 不需要使用按钮配置API 我只需要创建一个系统按钮 并将changesSelection AsPrimaryAction 设定为true 语义上 那就是弹出按钮的作用
接下来我想将 showsMenuAsPrimaryAction 设定为true 因为那也是语义上 弹出按钮的作用 但怎么会这样?按钮没有改变 其实有 我们达成了另一个切换按钮 但当然默认状态是关闭的
我们缺少一个非null菜单属性 那是在Mac Catalyst中获得 新的弹出外观的关键 现在我们已经将菜单设定好 我们的弹出按钮功能完善
最后 把焦点放在这些 像面板的大型切换按钮 这个布局善用iPad风格按钮 会延展填满它的所在空间的特色 使用Mac风格的按钮不会这样做 我能够保有iPad行为 就算Trip Planner设定成为Mac优化 方法如下 我先是又用了一个着色按钮配置
然后我用这个配置设定一个图像 不需要为这个图像指定按钮状态
通过指定.pad行为风格 我获得了iPad上会获得的布局行为 更明确地说 按钮的背景会延展 填满它的框架
符号需要再大一点 所以我在按钮上设定字体较大的 首选符号配置
跟之前一样 我将 changesSelectionAsPrimaryAction 设定为true 并提供了一个 colorUpdateHandler 以基于按钮的isSelected属性 指定选择和未选择的颜色
我知道你跟我一样 看到这么多新选项很兴奋 推出已久的UIButton API 和全新的属性可以许多方式混搭 创建许多种 在Mac上很自然的按钮 我想示范最后一项 Trip Planner的功能 那就是打印 有了新的内置命令键支持 在Catalyst上打印比以前更容易 我能够增强 Trip Planner中的打印体验 方法是用响应链 选择最适合 处理打印的对象 让我示范这是什么意思 回到最新版本的Trip Planner 我选了我在西班牙最爱的餐厅之一 无聊熟食店 我要通过单击Catalyst 特定的缩放控制 和地图视图交互
应用程序焦点和第一响应对象状态 已移动到这个细节视图控制器 当我从菜单选择文件打印 会出现打印对话框…
提议打印就这一个项目 我现在也要选择 高迪公园和巴塞罗那酒店 以完成这一天的规划
我会用内置快捷方式command+P打印
现在跳出的打印对话框提议打印 全部三个已选项目…
不只是目前的细节视图控制器 最后 如果我按Shift+Tab 把焦点移到边栏中的国家 而且在附属视图 或细节视图控制器 并未选择任何东西…
然后尝试打印 Trip Planner知道该怎么做 它会打印我在边栏中选择的所有项目
所以要怎么做到? 首先 杰森介绍的新info.plist键 一定要设定 让系统知道Trip Planner 支持打印 并想添加那个菜单项目 细节视图控制器中 我推翻来自UIResponder的两个方法 printContent负责实际打印 而UIKit会在响应链中寻找 能执行这个操作的对象 某些情况下 受到应用程序业务逻辑指示 细节视图控制器可能不想要打印
所以我用 canPerformAction这个方法 告知响应链 细节视图控制器是否可以打印 这个方法叫做在printContent之前 如果它返回false 细节视图控制器会被跳过 随着UIKit爬响应链 寻找可以执行这个操作的对象 在BrowserSplitViewController中 这是应用程序的根视图控制器 因此几乎总是在响应链的顶端 我实施了另一个UIResponder方法 targetForAction:withSender 这让拆分视图控制器 选择哪个对象应该负责打印 这很有用 当用户的选择和业务逻辑 指示我们应该打印某一组页数 但实施那项特定打印的对象 不在响应链里面 这是比较好的作法 相较之下 通过将becomeFirst Responder传送到那个对象 将对象塞进响应链就没那么适合 在这个场景里那会是反模式 以上就是打印的部分 看来我准备好开始脱离世俗去旅行了 在这支视频中 杰森向我们介绍了 新的Mac Catalyst API 和增强的Mac Catalyst API 它们能帮忙把你的应用程序 带到Mac上 而且比从前做得更好 然后我示范了如何在Trip Planner中 采用那些API 大多数案例中 它们只是新属性 只要设定一行代码 就能将典型的Mac特色 带进你的Catalyst应用程序 试试看吧 对代码做出一些微小的改变 看着你的Mac Catalyst应用程序 获得大幅改善 感谢收看 [打击音乐]
-
-
2:26 - Push Button
let button = UIButton(type: .system)
-
2:29 - Toggle Button
let button = UIButton(type: .system) button.changesSelectionAsPrimaryAction = true
-
2:40 - Pull-down Menu
let button = UIButton(type: .system) button.menu = UIMenu() button.showsMenuAsPrimaryAction = true
-
2:48 - Pop-up Button
let button = UIButton(type: .system) button.menu = UIMenu() button.showsMenuAsPrimaryAction = true button.changesSelectionAsPrimaryAction = true
-
3:50 - UIToolTipInteraction
let toolTipInteraction = UIToolTipInteraction(defaultToolTip: string) view.addInteraction(tooltipInteraction)
-
4:06 - UIControl ToopTip
control.toolTip = "Enable updates"
-
4:44 - ToolTips: UILabel
label.showsExpansionTextWhenTruncated = true
-
4:52 - Printing APIs
<key>UIApplicationSupportsPrintCommand</key> </true>
-
5:44 - Print Support
func printContent(_ sender: Any?) { let printInteractionController = UIPrintInteractionController.shared ... }
-
6:01 - Window Subtitle
scene.subtitle = "My subtitle"
-
7:34 - Behavioral Style
let button = UIButton(configuration: config) button.preferredBehavioralStyle = .pad
-
7:43 - Window Tab Opt-Out
<key>UIApplicationSceneManifest</key> <dict> <key>UIApplicationSupportsMultipleScenes</key> <true/> <key>UIApplicationSupportsTabbedSceneCollection</key> <false/> </dict>
-
8:23 - UIPointerShape
UIPointerShape.beam(preferredLength:0 axis: .horizontal) UIPointerShape.beam(preferredLength:0 axis: .vertical)
-
8:33 - Hidden Cursor
UIPointerStyle.hidden
-
13:25 - Scene subtitles
let subtitle: String = "..." let scene: UIScene = ... scene.subtitle = subtitle
-
14:54 - ToolTips
// ToolTip Interaction let imageView: UIImageView = UIImageView(frame: .zero) let interaction = UIToolTipInteraction(defaultToolTip: "...") imageView.addInteraction(interaction) // ToolTips - Label Expansion Text let label: UILabel = UILabel() label.text = "..." label.showsExpansionTextWhenTruncated = true // ToolTips — On UIControls let switchControl = UISwitch() switchControl.toolTip = "..."
-
17:49 - Primary button
let submitButton = UIButton(type: .system) submitButton.role = .primary submitButton.setTitle("Submit", for: .normal)
-
18:06 - Toggle button with menu
// Toggle button with menu let toggleButton = UIButton(configuration: .filled(), primaryAction: nil) toggleButton.configuration?.title = "Points Multiplier" toggleButton.changesSelectionAsPrimaryAction = true toggleButton.menu = ... // Elsewhere... toggleButton.configuration?.baseBackgroundColor = .systemRed
-
19:09 - Plain, destructive button
let resetButton = UIButton(configuration: .plain(), primaryAction: nil) resetButton.configuration?.title = "Reset" resetButton.role = .destructive resetButton.tintColor = .systemRed
-
19:36 - Pop-up button
let popup = UIButton(type: .system) popup.changesSelectionAsPrimaryAction = true popup.showsMenuAsPrimaryAction = true popup.menu = ...
-
21:01 - iPad behavioral style toggle
let button = UIButton(configuration: .filled(), primaryAction: nil) button.configuration?.image = UIImage(systemName: "leaf") button.preferredBehavioralStyle = .pad button.configuration?.preferredSymbolConfigurationForImage = UIImage.SymbolConfiguration(pointSize: 60) button.changesSelectionAsPrimaryAction = true button.configurationUpdateHandler = colorUpdateHandler
-
24:21 - Printing
override func printContent(_: Any?) { ... } override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool { if action == #selector(self.printContent(_:)) { ... } else { return super.canPerformAction(action, withSender: sender) } } override func target(forAction action: Selector, withSender sender: Any?) -> Any? { switch action { case #selector(UIResponder.printContent(_:)): ... default: return super.target(forAction: action, withSender: sender) } }
-
-
正在查找特定内容?在上方输入一个主题,就能直接跳转到相应的精彩内容。
提交你查询的内容时出现错误。请检查互联网连接,然后再试一次。