iOS 27 兼容放大模式下,keyboardFrame 与 UIScreen.bounds 的坐标差异

一个仍使用旧版 Launch Image 的 App,在 iOS 26 上没有收到异常反馈,到了 iOS 27,点击输入框唤起键盘,再点击页面空白处收起,整个页面向下偏移,顶部留下一大片空白。

真机采样发现:同一个 App 进程里,UIScreen.mainScreen.bounds 返回 375 × 812,键盘通知返回的宽度却是 430,收起后的顶部 Y 坐标是 932。

这里的 812 正是 Screen API 返回的高度,用它推算键盘位置,仍然会算错。

同一个 App 里,Screen 与键盘通知返回了什么?

这台设备竖屏的逻辑屏幕尺寸是 430 × 932。应用内采样如下,所有数值的单位都是点,也就是 pt:

数据来源 运行时返回值
UIScreen.mainScreen.bounds {{0, 0}, {375, 812}}
Window 和页面 bounds {{0, 0}, {375, 812}}
键盘弹起后的 UIKeyboardFrameEndUserInfoKey {{0, 539}, {430, 393}}
键盘收起后的 UIKeyboardFrameEndUserInfoKey {{0, 932}, {430, 393}}

对这个竖屏、底部停靠键盘的样本,键盘宽度 430 与 Screen 宽度 375 不同;弹起时的键盘底边是 539 + 393 = 932,也对不上 Screen 高度 812。收起后的 y=932 表示键盘顶部已退到屏幕底部,键盘自身的大小仍是 430 × 393。

两边的数值都是 pt,除以 @3x 也无法消除这组坐标差异。

Apple 对 keyboardFrameEndUserInfoKey 的说明指出,键盘 frame 属于屏幕坐标系,需要参与视图布局时应转换到目标视图坐标系,再计算交集。Apple 文档:keyboardFrameEndUserInfoKey

本次没有同一台设备升级前后的对照采样,不能倒推出"老系统返回 812,iOS 27 改成了 932",也不能将这个样本推广到所有 App。

Screen 为什么仍然返回旧尺寸?

工程中存在 LaunchScreen.storyboard 文件,但产物没有配置 UILaunchStoryboardName,也没有采用 UILaunchScreen,实际使用的是旧版 Launch Image。对应全面屏的竖屏启动图条目仍是 375 × 812,与应用读到的 Screen、Window 和页面尺寸相同。

这与旧 App 的兼容放大布局表现一致:应用以旧的逻辑尺寸排版,再放大显示到设备屏幕上。这里的"兼容放大"指应用适配行为,与用户设置中的"显示缩放"不同。

Apple 在介绍启动适配时提到,旧版 Launch Images 需要为每一种支持的屏幕尺寸提供图片,并随着新尺寸增加而更新;启动屏 Storyboard 则是推荐的适配方式。WWDC:Modernizing Your UI for iOS 13

启动配置是背景线索,但本次没有做仅切换启动屏的对照实验,尚不能认定更换启动屏就能解决问题。旧代码的错误则可以直接复算。

一段同时算错弹起和收起的代码

旧逻辑监听 UIKeyboardWillChangeFrameNotification,简化后如下。liftDistance 是页面自己维护的上移距离,不是系统提供的键盘高度:

objective-c 复制代码
CGRect keyboardFrame =
    [notification.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];

CGFloat keyboardY = CGRectGetMinY(keyboardFrame);
CGFloat screenHeight = CGRectGetHeight(UIScreen.mainScreen.bounds);

if (keyboardY == screenHeight - CGRectGetHeight(keyboardFrame)) {
    self.view.transform =
        CGAffineTransformMakeTranslation(0, -liftDistance);
} else {
    self.view.transform = CGAffineTransformMakeTranslation(
        0, keyboardY - CGRectGetHeight(self.view.frame));
}

它用键盘坐标与 Screen 高度的等式判断键盘是否弹起,其他情况全部进入回落分支。

代入采样数据:

text 复制代码
弹起:539 == 812 - 393,判断为 false
误入 else:539 - 812 = -273

收起:932 - 812 = 120
写入向下 120 pt 的平移,没有恢复原位

弹起和收起两个分支都受到了影响。放宽浮点数相等判断也解决不了问题,因为参与计算的坐标本身就不能直接混用。

修复:按事件恢复状态,只移动内容容器

这个表单页原本采用固定上移策略。修复将监听改为 UIKeyboardWillShowNotification 和 UIKeyboardWillHideNotification,直接按显示、隐藏事件切换位置;只移动表单容器,避免修改控制器根视图。

核心代码如下。formView 是表单容器,keyboardLift 沿用页面自己维护的上移距离,与前面的 liftDistance 含义相同:

objective-c 复制代码
// 由键盘将显示、将隐藏通知调用。
- (void)handleKeyboardVisibility:(NSNotification *)notification {
    BOOL showing =
        [notification.name isEqualToString:UIKeyboardWillShowNotification];

    self.formView.transform = showing
        ? CGAffineTransformMakeTranslation(0, -self.keyboardLift)
        : CGAffineTransformIdentity;
}

示例省略了通知注册、当前页面焦点检查、键盘同步动画、页面退出复位和观察者清理;实际修复包含这些处理。隐藏时直接恢复单位变换,不再根据键盘坐标计算回落距离。

固定上移距离只适用于这类已有固定布局的页面。需要动态避让时,应先转换键盘坐标并计算交集,或在支持的系统版本上采用 UIKeyboardLayoutGuide。Apple 布局指南

验证时保留原来的触发条件

验证包保留原来的启动配置、Xcode 和 SDK 组合。修复后,UIScreen.mainScreen.bounds 仍为 375 × 812,键盘收起通知仍返回 y=932,但反复收放键盘和切换前后台后,页面已不再偏移。

相关推荐
茶底世界之下19 小时前
iPhone Duo 适配实战,Awaking 踩过的 8 个坑
ios·swiftui·swift
跨境数据猎手20 小时前
HTTP 响应体编码与压缩:识别、解码
网络协议·http·ios
也非非也21 小时前
iPhone 中国区 AI 落地:Apple 与阿里千问合作的技术观察
大数据·人工智能·ios·agent·iphone·智子
茶底世界之下1 天前
同一片泳池,清透还是奶油胶片?用 Awaking 做两版成片
ios
2501_916008891 天前
iOS App 图标、启动屏和多语言搭配 方法,上架前使用KXBuild一次做好 App 图标
ide·vscode·ios·objective-c·个人开发·swift·敏捷流程
2501_915918411 天前
iOS 推送通知怎么实现,从 APNs p8 密钥到 iOS 真机收到第一条推送
vscode·ios·objective-c·个人开发·swift·敏捷流程
茶底世界之下2 天前
雨夜街角裁成方图,人物与冷暖光还能共存吗?
ios
youdexiang2 天前
IOS录音APP拍照记录能和音频同步吗
ios·音视频
2501_915909062 天前
Windows 打开 Xcode 工程,编译调试 Xcode 项目并和 Mac 同事协作
ide·windows·vscode·macos·ios·个人开发·xcode
互联网江湖2 天前
iPhone Duo跌倒,国产折叠屏手机吃饱?
ios·智能手机·iphone