iOS 键盘收起时listView底部约束无法重置为0、视图未恢复原尺寸问题
问题原因
- 核心问题:重复添加约束导致冲突
SnapKit 中snp.makeConstraints的作用是给视图追加新约束,不会覆盖已存在的同类型约束。你在键盘弹出时新增了bottom.equalTo(-intersection.height)的底部约束,键盘收起时又新增了bottom.equalTo(0)的底部约束,两个优先级相同的约束存在冲突,Auto Layout 系统会随机选择一个生效,因此大概率无法正常恢复布局。同时你两次重复添加top/leading/trailing/edges约束,也会产生大量冗余约束,进一步影响布局逻辑。 - 次要问题:布局调用逻辑冗余
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
相关产品推荐
相关产品推荐

