UIResponder.keyboardWillShowNotification在曲面屏iOS设备上返回高度是否不准确?
解决曲面屏设备手动键盘避让高度偏大问题
我用UIResponder.keyboardWillShowNotification实现手动键盘避让,在iPhone SE这类方形屏设备上运行正常,但在iPhone 15 Pro Max这类曲面屏设备上,返回的键盘高度明显偏大。尝试结合容器底部安全区高度调整,但数值并不匹配。
问题代码
import SwiftUI struct ContentView: View { @State private var text = "" @State private var keyboardHeight: CGFloat = .zero var body: some View { VStack { TextField("Test", text: $text) Color.red .frame(height: keyboardHeight) } .animation(.default.delay(0), value: keyboardHeight) .frame(maxHeight: .infinity, alignment: .bottom) .ignoresSafeArea(.keyboard) .onReceive(NotificationCenter.default.publisher(for: UIResponder.keyboardWillShowNotification)) { keyboardHeight = ($0.userInfo![UIResponder.keyboardFrameEndUserInfoKey] as! CGRect).height } .onReceive(NotificationCenter.default.publisher(for: UIResponder.keyboardWillHideNotification)) { _ in keyboardHeight = .zero } } }
效果对比
- 曲面屏设备(异常):

- 方形屏设备(正常):

问题原因
UIResponder.keyboardFrameEndUserInfoKey返回的是键盘在全局屏幕坐标系中的高度,曲面屏设备(如iPhone 15 Pro Max)的屏幕底部包含灵动岛相关的系统预留区域,这部分区域不会被SwiftUI视图占用,但键盘高度计算时包含了这部分空间。直接使用该高度会导致视图上移过多,出现不必要的空白。
解决方案
通过GeometryReader获取当前视图的底部安全区高度,结合窗口安全区信息,从键盘全局高度中剔除系统不可用区域,计算出视图实际需要的避让高度。
修改后的代码:
import SwiftUI struct ContentView: View { @State private var text = "" @State private var keyboardHeight: CGFloat = .zero var body: some View { GeometryReader { geometry in VStack { TextField("Test", text: $text) Color.red .frame(height: keyboardHeight) } .animation(.default.delay(0), value: keyboardHeight) .frame(maxHeight: .infinity, alignment: .bottom) .ignoresSafeArea(.keyboard) } .onReceive(NotificationCenter.default.publisher(for: UIResponder.keyboardWillShowNotification)) { notification in guard let keyboardFrame = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect else { return } guard let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene, let mainWindow = windowScene.windows.first else { return } // 剔除键盘高度中包含的屏幕底部系统预留区域 let validKeyboardHeight = keyboardFrame.height - mainWindow.safeAreaInsets.bottom keyboardHeight = max(validKeyboardHeight, 0) } .onReceive(NotificationCenter.default.publisher(for: UIResponder.keyboardWillHideNotification)) { _ in keyboardHeight = .zero } } }
关键说明
- 安全区适配:通过窗口安全区信息,剔除键盘高度中不属于视图交互区域的系统预留部分,确保避让高度精准匹配视图可用空间。
- 边界处理:用
max(validKeyboardHeight, 0)避免出现负数高度,防止布局异常。
内容的提问来源于stack exchange,提问作者pinglock
相关产品推荐
相关产品推荐

