大多数浏览器和
Developer App 均支持流媒体播放。
-
跟上键盘发展
键盘每年都在不断发展,以支持越来越多的语言、尺寸和功能。你可以在此学习如何设计跟上键盘的发展的 App,并适应各种设备显示方式。我们将向你展示如何打造流畅的文本输入功能,并分享重要的架构更改信息,帮助你了解键盘在系统中的工作原理。
章节
- 0:00 - Introduction
- 1:29 - Out of process keyboard
- 3:47 - Design for the keyboard
- 14:06 - New text entry APIs
- 15:06 - Key takeaways
资源
- Adding a search interface to your app
- Adjusting your layout with keyboard layout guide
- FocusState
- Human Interface Guidelines: Keyboards
- SafeAreaRegions
相关视频
WWDC23
WWDC21
-
下载
♪ ♪
Spencer:大家好 我是 Spencer Lewson 今天我要向大家介绍一下 键盘在这些年间的变化 以及开发者 如何设计“跟上键盘发展”的 App
众所周知 iPhone 的键盘 最早是在 2007 年推出的 之后经过了相当大的迭代升级 如今 iPhone 键盘支持许多种语言 适配每种语言可能涉及到的 不同布局尺寸 当然 键盘也应用到了很多其他设备中 正如键盘一样 操作系统也在不断升级 新增了多任务处理、 悬浮式键盘等令人激动的功能 达成了键盘的跨 App 应用
我们在去年推出了台前调度功能 将 iPad 用户的生产力 提高到了一个全新的水平 如今 多个场景的多屏显示及运行 前所未有地提高了 对配套硬件键鼠的需求 因此我们今天来介绍一下 我们是如何通过架构重组 来实现键盘在多样化场景的应用 以及这对你开发的 App 有何影响 我们还将分享一些 设计 App 时的建议和诀窍 帮助开发者花费尽量少的精力 尽可能无缝接入键盘功能 最后 我们还将介绍我们在 文本输入上一些精彩的新功能 让我们来谈一谈新的进程外键盘
将键盘与 App 独立开来 能够提高安全性 保障用户的输入内容隐私 同时释放 App 和 整个系统的内存 因为这样就能用一个键盘 代替多个 App 内分别运行的 多个键盘实例 有了这一新架构 我们就可以做出面向未来的设计 实现更多令人激动的新功能 让我们来介绍一下 这背后的运行原理吧 在 iOS 17 问世前 键盘视图和逻辑 是放在 App 进程中执行的 而到了搭载 iOS 17 的 iPhone 上 键盘拥有了独立的进程 几乎完全在 App 之外运行 我十分想要将全部运行原理 分享给大家 但首先 我们来聊聊进程内键盘的运行原理 这样就能看出二者的区别了
进程内运行时 你的 App 会首先向键盘发送请求 例如在触控后调用 becomeFirstResponder 由此启动一系列工作同步进行 初始化所有视图 完成后 系统将播放 调出键盘的动画 这时 App 进入待机模式 或继续执行 App 侧工作 直到触控事件发生 并生成输入文本
进程外运行则略有不同 当 App 请求键盘时 同理 可通过 调用 becomeFirstResponder 来完成 你的 App 将首先执行一些初始计算 接下来 键盘进程才会开始初始化 UI 此时 App 进入待机状态 或继续执行 App 侧工作 UI 初始化完成后 调出键盘 并进行两个进程的动画协同 键盘 UI 显示出来后 键盘范围内发生的所有触控事件 将转换为文本输入到 App 中 对大多数 App 来说 这些改动是完全透明的 无需开发者主动采用 不过 新的异步方式的各方面如今在 整个键盘进程中均有体现 可能会带来些许时序上的差异 因此 如果你的 App 容易受到 文本输入时序、 选择更改或其他文本相关操作影响 且影响较大 你需要将这一新架构牢记在心 讲完 iOS 系统 在输入上的差别后 让我们来谈谈 开发者在设计 App 时 需要关注的相对较新的场景 最常见的用例大家当然都很熟悉: 即带有键盘的全屏 App 这一用例较为简单明了 App 和键盘均为全屏显示 因此 要适配键盘 只需将 App 的视图 向上移动一个键盘高度值即可 而使用台前调度后 系统不再遵循这种模式 高级多任务处理功能 不再要求 App 全屏显示 也就是说 调出键盘后 要正确调整 App 视图 有些地方需要特别注意 这是因为键盘和 App 的视图 不再呈对齐状态 必须进行额外变形 根据使用场景适当调整 App 例如在这个场景中 App 视图需要调整的值并不是 键盘的高度 Y 而是键盘和 App 重叠部分的高度 即 Y' 需要计算的调整值 甚至可能不止这一个 屏幕上显示多个场景时 要针对每个场景进行计算和调整 硬件键盘的相关场景也有更新 接入硬件键盘后 屏幕上会弹出居中的辅助工具栏 全尺寸显示的工具栏 属于键盘的一部分 调整 App 视图时应为其留出空间 轻拂可将工具栏最小化 台前调度未启用时 我们保留了现有行为 即迷你工具栏不作为键盘的一部分 且会与 App 视图重叠 用户如需访问被遮挡的内容 直接将工具栏拖至 屏幕另一侧即可 而台前调度启用后 迷你工具栏也属于键盘的一部分 根据使用场景的不同 开发者可以选择更新滚动偏移量、 上拉输入辅助视图、 或根据需要做出其他布局调整 我们明白 这涉及非常多 需要开发者考虑的场景和细节 不过好消息是 使用正确的 API 可以让系统帮你完成大部分工作 那么让我们来聊一聊键盘布局指南 你可能对 随着 iOS 15 一起推出的 键盘布局指南很熟悉了 它采用了一个简单的自动布局指南 可以自动适配键盘 去年一年中 我们也不断在更新内容 事实上 这一指南也应用在了 一些复杂的 Apple App 中 例如聚焦和信息 我们推荐这一方案 是因为它操作简单 只需在 App 视图与指南之间 添加一行约束即可 现在我们来看看这一方案 给开发者带来了怎样的好处 现有的默认行为如下: 当屏幕键盘入坞时 键盘布局指南会跟随键盘而定 入坞是指键盘贴放至屏幕最底端 而键盘未入坞 比如悬浮在 iPad 屏幕上时 则指南将放置在 视图底部安全区域之上 最后 当指南范围内出现触控点时 指南会跟踪识别键盘关闭手势 我很高兴地告诉大家 我们在 iOS 17 中 拓展了定制选项 开发者可通过更改设置 得到符合预期的行为 UIKeyboardLayoutGuide 目前具备三种属性 首先是 followsUndockedKeyboard 指南会默认将悬浮键盘或迷你工具栏 视作画面外键盘一样处理 不过 当属性设置为 true 时 哪怕键盘处于悬浮模式 只要键盘位于 App 窗口上方 指南都会紧贴键盘 其次是 usesBottomSafeArea 关闭键盘后 键盘布局指南会默认 沿安全区域的高度放置 不过当属性设置为 false 时 usesBottomSafeArea 会转而置于视图底端 在这个示例中 视图底端即屏幕底端 这一属性在哪些情况下有用呢? 它可以扩展背景 使得在键盘关闭后 屏幕下方依然有背景覆盖 并且可以随着键盘启用做出调整 与 InputAccessoryView 行为相仿 事实上 此属性这一用例十分有趣 我们来讲解一下它的工作原理 有了这几行代码 我们能得到这一简单的类辅助输入视图 配有文本栏 并将其固定在底部安全区域上方; 同时 关闭键盘后 背景将会覆盖到 视图的最底部 请注意 这一设置仅针对垂直约束 因为这一用例有趣的点就在这里 先设置 usesBottomSafeArea 为 false 将文本栏固定至背景顶部 二者的间距设置为 系统间距以作填充 接下来 为使文本栏 固定于键盘上方 需要把指南顶端约束至文本栏底端 二者间至少保持一个系统间距 这里的 greater than 十分重要 当键盘在画面外 其文本栏需要更加灵活 才能固定在底部安全区域上方 另外 还需要将指南的顶端锚定点 绑定至背景底端 这样可以保证 当指南位于画面之外时 背景会向下延伸至屏幕最下方 不过前提是 usesBottomSafeArea 设置为 false 最后 把底部安全区域 约束至文本栏底端 并留出系统间距 这里我们使用的约束 还是 greater than 以确保文本栏足够灵活 可以在键盘被调出后跟随其位置 如此一来 可调节视图就完成了 这一视图中 背景可延伸至最下; 文本栏可随时调整位置 始终位于键盘上方并保持系统间距; 背景也始终位于键盘上方 正如输入辅助视图的效果一样
第三是 keyboardDismissPadding 这一属性设置的是 关闭手势的滚动参数 如果你之前尝试过 使用键盘布局指南 创建类 InputAccessory 视图 你可能已经注意到了 键盘关闭手势 只在触控点落入键盘范围后才开始 我们可以用这一新属性 来解决这个问题
keyboardDismissPadding 可以让开发者设置 键盘上方可响应关闭手势 的填充区域大小 这个相对简单易懂些 只要将视图高度拖拽至你需要的高度 并设置属性 就完成了 这样一来 触控点在视图范围内即可开始关闭键盘 当然了 UIKit 并不是 构建 App 的唯一框架 我们还有 SwiftUI 幸运的是 SwiftUI 可自动处理常见用例 SwiftUI 将键盘 作为安全区域的一部分 键盘关闭后 系统将跟踪屏幕底部的桌面指示器 调出键盘时 系统播放动画 调整安全区域 自动调节视图尺寸 因此开发者基本不需要 编写键盘代码 不过 你可能需要 继续调整一下布局 确保更改尺寸或位置后的视图 符合你的要求 SwiftUI 提供了很多优秀的资源 在这里我无法一一列举出来 如需进一步了解更多 请查看下方文档链接 接下来我将介绍一种 更偏人工的键盘整合方式 键盘通知 在 SwiftUI 和 键盘布局指南投入使用前 将键盘整合进 App 的唯一方式 就是监听一组固定的键盘通知: 包括 willShow、didShow、 willHide 和 didHide 而后根据通知中的区域和动画信息 手动调整布局 键盘通知仍然有效 只是操作时需要格外谨慎 因为这部分工作系统无法帮你处理 台前调度引入后 我们发现 有一种 常用的处理模式不再百试百灵 且出错趋势有所上升 这一模式通常以接收键盘通知为重 并直接使用键盘高度的原始值
还记得之前谈到过的 键盘高度和 App 在屏幕上的位置 之间的交互细节吗? 让我们来看看使用通知后 交互是如何进行的 每条通知会指定 键盘在屏幕坐标中的位置 得出键盘所占的预期区域 如果屏幕的坐标空间 与 App 的坐标空间相符 比如在 App 全屏显示的情况下 通知里的原始高度值 将使 App 视图得到与预期相符的调整 不过这只是巧合 而当屏幕的坐标空间 与 App 的不相符时 通知里给出的原始高度值就不再是 调整 App 视图的正确数值了 视图可能会出现上拉过高 或位置错误的问题 幸好 修改通知处理方式 可以让这一进程重回正轨 iOS 16.1 为键盘通知 新增了一项内容 即对应的 UIScreen 作为通知的对象 首先 我们用它来判断 键盘是否与 App 同屏显示 如否 则无需任何调整 接下来 计算出一个矩形 代表键盘相对于视图的位置 通过获取键盘的预期区域范围 以及通知和视图中给出的坐标空间 即可得出结果 之后利用这一结果 将 keyboardFrameEnd 转换成坐标空间 这一新矩形可以帮助我们确定 App 视图的必要偏移量 计算其视图和 convertedKeyboardFrameEnd 的重叠量即可 若 App 和键盘视图出现重叠 必要偏移量等于 App 和键盘视图 重叠部分的高度值 有了这个 开发者就可以 随心所欲地调整约束或布局了 现在 在新的进程外架构下 通知行为可能会有些许变化 开发者应当加以了解 我们来花点时间讲解一下 还记得这张键盘进程生命周期 的概述图表吗? 让我们重点关注一下动画阶段中 名为“调出动画”的进程 在进程内架构中 App 请求键盘时 系统会同步初始化键盘 UI 发布通知并播放动画 不过 在新的进程外架构中 App 请求键盘时 系统异步初始化键盘 UI 接下来异步发布通知 并播放动画 这将带来一些细微的时序差异 因此 若你的 App 依靠 通知的发布时序 作为调用 becomeFirstResponder 后的某种“回调” 或者主线程上有重要工作正在执行 并可能导致通知处理延后的话 那么新模式可能影响你的 App 需要你牢记 我们已经介绍过所有建议和诀窍了 接下来 为了尽可能简化用户 在 App 中打字的操作 我们推出了一款令人激动的 新功能及 API 可以进一步加快文本输入速度 即字间预测 iOS 17 的英文键盘会预测 你接下来可能输入的文字 并直接显示在文本栏当中 这些预测内容是由 当前设备安全生成的 并只使用当前文本栏中的 上下文信息 采用预测内容的操作也十分简单 我们在这里使用了 UITextInputTraits 协议 如你所见 此处已经新增一条 inlinePredictionType 属性 并附有几个选项 默认情况下 字间预测在 大多数文本输入栏中均为启用 但在不适用预测的文本栏中 会自动关闭 比如搜索栏或密码栏等 当然 开发者 可以定制 App 内行为 明确将这一属性 设置为 yes 或 no 好了 让我们来回顾一些关键要点 你需要记住的是: 设计能与键盘无缝协作的 App 不管它采取的是何种显示方式 编写受时序影响强的代码时 牢记新的进程外键盘模型 还有使用 API 提高文本输入速度 优化 App 使用体验 感谢大家跟随键盘一起发展 ♪ ♪
-
-
6:21 - Keyboard layout guide
view.keyboardLayoutGuide.topAnchor.constraint(equalTo: textView.bottomAnchor).isActive = true
-
7:56 - usesBottomSafeArea
// Example of using usesBottomSafeArea to create keyboard and text view aligned with safe area view.keyboardLayoutGuide.usesBottomSafeArea = false textField.topAnchor.constraint(equalToSystemSpacingBelow: backdrop.topAnchor, multiplier: 1.0).isActive = true view.keyboardLayoutGuide.topAnchor.constraint(greaterThanOrEqualToSystemSpacingBelow: textField.bottomAnchor, multiplier: 1.0).isActive = true view.keyboardLayoutGuide.topAnchor.constraint(equalTo: backdrop.bottomAnchor).isActive = true view.safeAreaLayoutGuide.bottomAnchor.constraint(greaterThanOrEqualTo: textField.bottomAnchor).isActive = true
-
9:40 - Keyboard dismiss padding
var dismissPadding = aboveKeyboardView.bounds.size.height view.keyboardLayoutGuide.keyboardDismissPadding = dismissPadding
-
12:11 - Handle willShow or hideKeyboard notifications
func handleWillShowOrHideKeyboardNotification(notification: NSNotification) { // Retrieve the UIScreen object from the notification (Added iOS 16.1) guard let screen = notification.object as? UIScreen else { return } // Determine if the notification’s screen corresponds to your view’s screen guard(screen.isEqual(view.window?.screen)) else { return } // Calculate intersection with keyboard let endFrameKey = UIResponder.keyboardFrameEndUserInfoKey // Get the ending screen position of the keyboard guard let keyboardFrameEnd = userInfo[endFrameKey] as? CGRect else { return } let fromCoordinateSpace: UICoordinateSpace = screen.coordinateSpace let toCoordinateSpace: UICoordinateSpace = view // Convert from the screen coordinate space to your local coordinate space let convertedKeyboardFrameEnd = fromCoordinateSpace.convert(keyboardFrameEnd, to: toCoordinateSpace) // Calculate offset for view adjustment var bottomOffset = view.safeAreaInsets.bottom // Get the intersection between the keyboard's frame and the view's bounds let viewIntersection = view.bounds.intersection(convertedKeyboardFrameEnd) // Check whether the keyboard intersects your view before adjusting your offset. if !viewIntersection.isEmpty { // Set the offset to the height of the intersection bottomOffset = viewIntersection.size.height } // Use the new offset to adjust your UI movingBottomConstraint.constant = bottomOffset // Adjust view layouts and animate using information in notification ... }
-
14:38 - Inline predictions
@MainActor public protocol UITextInputTraits : NSObjectProtocol { // Controls whether inline text prediction is enabled or disabled during typing @available(iOS, introduced: 17.0) optional var inlinePredictionType: UITextInlinePredictionType { get set } } public enum UITextInlinePredictionType : Int, @unchecked Sendable { case `default` = 0 case no = 1 case yes = 2 } let textView = UITextView(frame: frame) textView.inlinePredictionType = .yes
-
-
正在查找特定内容?在上方输入一个主题,就能直接跳转到相应的精彩内容。
提交你查询的内容时出现错误。请检查互联网连接,然后再试一次。