iOS键盘弹出/收起时屏幕偏移异常问题求助
修复键盘弹出/收起时的屏幕偏移与底部空白问题
我来帮你搞定这个键盘偏移的问题!你遇到的多次操作后屏幕逐渐偏移、键盘弹出后底部出现空白的问题,主要是当前代码里的两个关键问题导致的,咱们一步步来修复:
问题根源
- 错误的键盘Frame获取键:你用了
UIKeyboardFrameBeginUserInfoKey,这个值是键盘开始显示时的初始位置,而键盘显示是有动画过渡的,这个初始位置的高度并不等于最终显示在屏幕上的键盘高度,多次触发后就会导致偏移累加。 - 偏移逻辑的判断漏洞:直接通过
self.view.frame.origin.y == 0来判断是否需要偏移,一旦键盘高度发生变化(比如切换到表情键盘),这个判断就会失效,导致复位时的偏移量错误,最终屏幕越来越偏。
修复方案
方案1:修正现有代码逻辑
我们需要改用UIKeyboardFrameEndUserInfoKey获取键盘最终的显示高度,同时和键盘动画同步,并且确保复位时准确回到初始位置:
override func viewDidLoad() { super.viewDidLoad() // 注册键盘通知(兼容iOS各版本写法) NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: UIResponder.keyboardWillShowNotification, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name: UIResponder.keyboardWillHideNotification, object: nil) } @objc func keyboardWillShow(notification: NSNotification) { // 获取键盘最终的显示frame guard let keyboardFrame = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue else { return } let keyboardHeight = keyboardFrame.cgRectValue.height // 仅当视图在初始位置时执行偏移,避免重复操作 if self.view.frame.origin.y == 0 { // 同步键盘动画的时长与曲线,保证视觉流畅 let duration = notification.userInfo?[UIResponder.keyboardAnimationDurationUserInfoKey] as? Double ?? 0.25 let curve = notification.userInfo?[UIResponder.keyboardAnimationCurveUserInfoKey] as? UInt ?? UIView.AnimationOptions.curveEaseInOut.rawValue UIView.animate(withDuration: duration, delay: 0, options: UIView.AnimationOptions(rawValue: curve)) { self.view.frame.origin.y -= keyboardHeight } } } @objc func keyboardWillHide(notification: NSNotification) { // 直接复位到初始位置,不依赖键盘高度,彻底避免累加错误 if self.view.frame.origin.y != 0 { let duration = notification.userInfo?[UIResponder.keyboardAnimationDurationUserInfoKey] as? Double ?? 0.25 let curve = notification.userInfo?[UIResponder.keyboardAnimationCurveUserInfoKey] as? UInt ?? UIView.AnimationOptions.curveEaseInOut.rawValue UIView.animate(withDuration: duration, delay: 0, options: UIView.AnimationOptions(rawValue: curve)) { self.view.frame.origin.y = 0 } } }
方案2:更优的AutoLayout方式(推荐)
如果你的界面是用AutoLayout构建的,直接修改view的frame容易和约束冲突,更推荐通过调整底部约束的constant值来实现:
- 给主视图底部添加一个约束,关联为IBOutlet:
@IBOutlet weak var bottomConstraint: NSLayoutConstraint!
- 修改通知处理代码:
@objc func keyboardWillShow(notification: NSNotification) { guard let keyboardFrame = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue else { return } let keyboardHeight = keyboardFrame.cgRectValue.height let duration = notification.userInfo?[UIResponder.keyboardAnimationDurationUserInfoKey] as? Double ?? 0.25 let curve = notification.userInfo?[UIResponder.keyboardAnimationCurveUserInfoKey] as? UInt ?? UIView.AnimationOptions.curveEaseInOut.rawValue bottomConstraint.constant = keyboardHeight UIView.animate(withDuration: duration, delay: 0, options: UIView.AnimationOptions(rawValue: curve)) { self.view.layoutIfNeeded() } } @objc func keyboardWillHide(notification: NSNotification) { let duration = notification.userInfo?[UIResponder.keyboardAnimationDurationUserInfoKey] as? Double ?? 0.25 let curve = notification.userInfo?[UIResponder.keyboardAnimationCurveUserInfoKey] as? UInt ?? UIView.AnimationOptions.curveEaseInOut.rawValue bottomConstraint.constant = 0 UIView.animate(withDuration: duration, delay: 0, options: UIView.AnimationOptions(rawValue: curve)) { self.view.layoutIfNeeded() } }
方案3:如果使用UIScrollView(或子类如UITableView)
如果你的界面包含滚动视图,苹果官方推荐的方式是调整contentInset和scrollIndicatorInsets,这样滚动视图会自动适配键盘,同时避免偏移问题:
@objc func keyboardWillShow(notification: NSNotification) { guard let keyboardFrame = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue else { return } let keyboardHeight = keyboardFrame.cgRectValue.height // 调整滚动视图的内边距,适配键盘高度 scrollView.contentInset.bottom = keyboardHeight scrollView.scrollIndicatorInsets.bottom = keyboardHeight } @objc func keyboardWillHide(notification: NSNotification) { // 复位内边距 scrollView.contentInset.bottom = 0 scrollView.scrollIndicatorInsets.bottom = 0 }
额外注意事项
- 记得在
deinit里移除通知观察者,避免内存泄漏:
deinit { NotificationCenter.default.removeObserver(self) }
- iOS 13及以上,推荐使用
NotificationCenter的block注册方式,不需要手动移除观察者(系统会自动处理):
override func viewDidLoad() { super.viewDidLoad() NotificationCenter.default.addObserver(forName: UIResponder.keyboardWillShowNotification, object: nil, queue: .main) { [weak self] notification in // 处理键盘显示逻辑 } // 同理注册键盘隐藏的通知 }
内容的提问来源于stack exchange,提问作者user
相关产品推荐
相关产品推荐

