You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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值来实现:

  1. 给主视图底部添加一个约束,关联为IBOutlet:
@IBOutlet weak var bottomConstraint: NSLayoutConstraint!
  1. 修改通知处理代码:
@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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 07:16:48