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

iOS 键盘收起时listView底部约束无法重置为0、视图未恢复原尺寸问题

问题原因

  1. 核心问题:重复添加约束导致冲突
    SnapKit 中 snp.makeConstraints 的作用是给视图追加新约束,不会覆盖已存在的同类型约束。你在键盘弹出时新增了bottom.equalTo(-intersection.height)的底部约束,键盘收起时又新增了bottom.equalTo(0)的底部约束,两个优先级相同的约束存在冲突,Auto Layout 系统会随机选择一个生效,因此大概率无法正常恢复布局。同时你两次重复添加top/leading/trailing/edges约束,也会产生大量冗余约束,进一步影响布局逻辑。
  2. 次要问题:布局调用逻辑冗余
    keyboardWillHide中同时调用listView和父视图的约束更新、布局刷新方法,属于冗余调用,不会直接导致问题,但会增加不必要的性能开销。

修复方案

方案1:使用snp.remakeConstraints替换makeConstraints

remakeConstraints会先清空当前视图通过 SnapKit 添加的所有旧约束,再添加新约束,从根源避免约束冲突。修改后代码如下:

// 监听通知的代码不变
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 private func keyboardWillShow(notification: NSNotification) {
    if let keyboardFrame: NSValue = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue {
        let keyboardFrameEnd = keyboardFrame.cgRectValue
        let convertedKeyboardFrameEnd = self.view.convert(keyboardFrameEnd, from: nil)
        let intersection = convertedKeyboardFrameEnd.intersection(listView.frame)
        if intersection.size.height > 0 {
            // 替换为remakeConstraints
            self.listView.snp.remakeConstraints { make in
                make.top.leading.trailing.equalTo(0)
                make.bottom.equalTo(-intersection.height)
            }
            self.view.layoutIfNeeded()
        }
    }
}

@objc private func keyboardWillHide() {
    // 替换为remakeConstraints,edges.equalTo已经包含bottom约束,无需重复添加
    self.listView.snp.remakeConstraints { make in
        make.edges.equalTo(0)
    }
    self.view.layoutIfNeeded()
}

方案2:持有底部约束引用单独更新(性能更优)

如果listView的顶部、左右约束固定,只需要更新底部约束,可以单独持有底部约束的引用,避免每次重建所有约束:

// 首先在控制器中声明底部约束属性
var listViewBottomConstraint: Constraint?

// 初始化listView时绑定底部约束
listView.snp.makeConstraints { make in
    make.top.leading.trailing.equalTo(0)
    listViewBottomConstraint = make.bottom.equalTo(0).constraint
}

// 键盘弹出时仅更新底部约束的偏移量
@objc private func keyboardWillShow(notification: NSNotification) {
    if let keyboardFrame: NSValue = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue,
       let duration = notification.userInfo?[UIResponder.keyboardAnimationDurationUserInfoKey] as? Double {
        let keyboardFrameEnd = keyboardFrame.cgRectValue
        let convertedKeyboardFrameEnd = self.view.convert(keyboardFrameEnd, from: nil)
        let intersection = convertedKeyboardFrameEnd.intersection(listView.frame)
        if intersection.size.height > 0 {
            listViewBottomConstraint?.update(offset: -intersection.height)
            // 跟随键盘动画时长布局,效果更顺滑
            UIView.animate(withDuration: duration) {
                self.view.layoutIfNeeded()
            }
        }
    }
}

// 键盘收起时重置底部约束偏移量
@objc private func keyboardWillHide(notification: NSNotification) {
    if let duration = notification.userInfo?[UIResponder.keyboardAnimationDurationUserInfoKey] as? Double {
        listViewBottomConstraint?.update(offset: 0)
        UIView.animate(withDuration: duration) {
            self.view.layoutIfNeeded()
        }
    }
}

内容的提问来源于stack exchange,提问作者user3492121

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.05 21:51:03