WWDC Quick Look 💓 By SwiftGGTeam
Keep up with the keyboard

Keep up with the keyboard

观看原视频

Highlight

iOS 17 将键盘移至独立进程以提升安全性和内存效率,同时增强了 UIKeyboardLayoutGuide,新增 followsUndockedKeyboardusesBottomSafeAreakeyboardDismissPadding 属性,让开发者用 Auto Layout 约束即可处理浮动键盘、分屏场景和键盘收起动画,无需手动计算键盘高度。

核心内容

进程外键盘(Out-of-Process Keyboard)

iOS 17 中,键盘的视图和逻辑从 App 进程中独立出来。这带来两个好处:

  • 安全性:App 无法直接接触键盘进程中的用户输入,减少隐私泄露风险
  • 内存效率:整个系统只需一个键盘实例,而不是每个 App 各一份

对大多数 App 这个变化是透明的。但如果代码对文本输入、选择变化的时序特别敏感,需要注意新的异步行为。

Stage Manager 带来的布局挑战

全屏模式下,键盘适配很简单:把视图上移键盘高度即可。但 Stage Manager 打破了这一假设:

  • App 可能不是全屏的,键盘场景和 App 窗口不再对齐
  • 需要计算的是键盘和 App 窗口的交集高度,而不是键盘的完整高度
  • 多个窗口可能同时存在,每个都需要独立的适配计算

传统的 UIKeyboardWillShowNotification 方式在 Stage Manager 下需要额外处理屏幕坐标转换和多窗口场景。

UIKeyboardLayoutGuide 增强

iOS 15 引入的 UIKeyboardLayoutGuide 在 iOS 17 中获得三项新能力:

  • followsUndockedKeyboard:控制是否跟踪浮动键盘的位置变化
  • usesBottomSafeArea:控制键盘收起时布局是否回到安全区域底部
  • keyboardDismissPadding:设置键盘收起时的额外间距

Messages 和 Spotlight 都已迁移到 UIKeyboardLayoutGuide 方案,Apple 建议开发者优先使用这种方式,而非手动监听键盘通知。

详细内容

基础用法:Keyboard Layout Guide

06:21

// 将文本视图底部与键盘顶部对齐
view.keyboardLayoutGuide.topAnchor.constraint(
    equalTo: textView.bottomAnchor
).isActive = true

关键点:

  • keyboardLayoutGuideUIView 的属性,代表键盘在视图坐标系中的布局区域
  • topAnchor 对应键盘的顶部边界
  • 当键盘弹出时,topAnchor 自动上移,带动约束的视图一起移动
  • 当键盘收起时,topAnchor 回到屏幕底部,视图自动复位
  • 无需手动监听通知,Auto Layout 自动处理动画

控制安全区域行为:usesBottomSafeArea

07:56

// 禁用底部安全区域,让键盘收起时布局回到视图底部而非安全区域
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

关键点:

  • usesBottomSafeArea = false 让键盘收起时 keyboardLayoutGuide.topAnchor 回到视图底部,而不是安全区域底部
  • 这对需要填满整个屏幕的布局很重要
  • greaterThanOrEqualToSystemSpacingBelow 确保键盘和文本框之间至少有一个系统间距
  • 多条约束组合处理键盘弹出和收起两种状态

键盘收起间距:keyboardDismissPadding

09:40

var dismissPadding = aboveKeyboardView.bounds.size.height

// 设置键盘收起时的额外间距
view.keyboardLayoutGuide.keyboardDismissPadding = dismissPadding

关键点:

  • keyboardDismissPadding 控制键盘收起后,布局与屏幕底部之间的额外间距
  • 适用于键盘上方有工具栏或附加视图的场景
  • 当键盘收起时,这些附加视图需要保留在屏幕内

手动处理键盘通知(备用方案)

12:11

如果必须使用键盘通知,iOS 17 提供了更精确的屏幕级信息:

func handleWillShowOrHideKeyboardNotification(notification: NSNotification) {
    // 从通知获取 UIScreen 对象(iOS 16.1 新增)
    guard let screen = notification.object as? UIScreen else { return }
    
    // 确认通知的屏幕与当前视图所在屏幕一致
    guard screen.isEqual(view.window?.screen) else { return }
    
    // 获取键盘结束位置的屏幕坐标
    let endFrameKey = UIResponder.keyboardFrameEndUserInfoKey
    guard let keyboardFrameEnd = notification.userInfo?[endFrameKey] as? CGRect else { return }
    
    // 从屏幕坐标系转换到视图本地坐标系
    let fromCoordinateSpace: UICoordinateSpace = screen.coordinateSpace
    let toCoordinateSpace: UICoordinateSpace = view
    let convertedKeyboardFrameEnd = fromCoordinateSpace.convert(keyboardFrameEnd, to: toCoordinateSpace)
    
    // 计算键盘与视图的交集
    let viewIntersection = view.bounds.intersection(convertedKeyboardFrameEnd)
    
    var bottomOffset = view.safeAreaInsets.bottom
    
    // 如果键盘与视图相交,使用交集高度作为偏移量
    if !viewIntersection.isEmpty {
        bottomOffset = viewIntersection.size.height
    }
    
    // 更新约束并执行动画
    movingBottomConstraint.constant = bottomOffset
    
    // 使用通知中的动画参数执行布局更新
    // ...
}

关键点:

  • iOS 16.1 起,UIKeyboardWillShowNotificationobjectUIScreen,可以精确知道哪个屏幕的键盘变化
  • 必须用 screen.isEqual(view.window?.screen) 过滤,避免多窗口场景下的错误处理
  • 坐标转换必须用 UICoordinateSpace.convert(_:to:),不能直接用 CGRect 运算
  • 计算的是键盘与视图的交集高度,而不是键盘完整高度,这对 Stage Manager 分屏场景至关重要

内联预测:Inline Predictions

14:38

// 启用内联文本预测
textView.inlinePredictionType = .yes

// 禁用内联文本预测
textField.inlinePredictionType = .no

关键点:

  • inlinePredictionType 控制是否显示内联文本预测(灰色建议文字)
  • 三个选项:.default(系统决定)、.no(禁用)、.yes(启用)
  • 适用于密码输入框、验证码输入等不应显示预测的场景
  • 遵循 UITextInputTraits 协议的所有文本输入控件都支持

核心启发

1. 用 Keyboard Layout Guide 重构所有键盘适配代码

做什么:把项目中所有手动监听 UIKeyboardWillShowNotification 的代码替换为 UIKeyboardLayoutGuide 约束。

为什么值得做UIKeyboardLayoutGuide 自动处理浮动键盘、分屏、多窗口等复杂场景,代码量减少 80% 以上,且行为更可靠。

怎么开始:找到所有键盘通知的监听代码,删除通知注册和回调,在 viewDidLoad 中添加 keyboardLayoutGuide.topAnchor 约束。测试浮动键盘、分屏、旋转等场景。

2. 为 Stage Manager 优化多窗口应用

做什么:确保 iPad 多窗口场景下,每个窗口独立正确处理键盘弹出。

为什么值得做:Stage Manager 下多个 App 窗口可以同时显示,每个窗口都可能需要键盘适配。错误的实现会导致一个窗口的键盘影响另一个窗口的布局。

怎么开始:在 iPad 上开启 Stage Manager,打开应用的多个窗口,在每个窗口中触发键盘,验证布局只影响当前窗口。使用 notification.object as? UIScreen 过滤通知。

3. 为敏感输入禁用内联预测

做什么:在密码框、验证码框、信用卡号输入框中显式设置 inlinePredictionType = .no

为什么值得做:内联预测会在输入框中显示灰色建议文字,对于敏感信息这既是隐私问题也是用户体验问题。

怎么开始:遍历所有文本输入控件,对涉及敏感信息的字段设置 inlinePredictionType = .no。同时检查 autocorrectionTypeautocapitalizationType 是否也配置正确。

4. 实现键盘上方工具栏的平滑收起

做什么:在键盘上方放置自定义工具栏,键盘收起时工具栏平滑过渡到屏幕底部。

为什么值得做:很多应用(如笔记、文档编辑)在键盘上方有格式工具栏。keyboardDismissPadding 让这种布局无需手动计算即可正确工作。

怎么开始:设置 view.keyboardLayoutGuide.keyboardDismissPadding = toolbarHeight,然后将工具栏底部约束到 keyboardLayoutGuide.topAnchor

关联 Session

评论

GitHub Issues · utterances