Swift:键盘弹出时UIView首次无法移动的问题求助
问题分析:首次弹出键盘时视图不上移,二次点击恢复正常
嘿,我帮你排查了代码里的问题,这几个关键点导致了这个奇怪的现象:
核心原因:通知注册时机太晚
你把键盘通知的观察者注册放在了textViewDidBeginEditing(_:)方法里,而且还错误地嵌套在了UIView.animate的闭包中。
当你首次点击UITextView时,系统的执行顺序是这样的:
- 检测到UITextView被激活,准备弹出键盘
- 发送
UIKeyboardWillShow通知 - 执行
textViewDidBeginEditing(_:)方法,这时才注册通知观察者
也就是说,第一次键盘弹出的通知早就发完了,你的观察者根本没机会捕获到它,所以约束没有更新,视图自然不会上移。而第二次点击时,观察者已经在第一次编辑时注册好了,就能正常接收通知并更新约束。
其他两处代码问题
除了核心的通知时机问题,还有两个小bug:
- iOS版本适配逻辑错误:在
keyboardWillShow的else分支(iOS11以下),你把约束constant设成了view.safeAreaInsets.bottom,这完全不对——旧系统下应该直接用键盘高度值,不然约束等于没调整。 - 动画块误用:注册通知的代码不需要放在动画闭包里,动画应该用来包裹约束更新后的布局刷新,而不是注册监听的逻辑。
修复后的代码示例
class ChatViewController: UIViewController, CNContactPickerDelegate, UISearchBarDelegate, UITableViewDelegate, UITableViewDataSource, UIToolbarDelegate, UITextFieldDelegate, UITextViewDelegate { @IBOutlet weak var composeTextView: UITextView! @IBOutlet weak var composeViewBottomConstraint: NSLayoutConstraint! override func viewDidLoad() { super.viewDidLoad() composeTextView.delegate = self // 提前在viewDidLoad里注册键盘通知,而不是等到编辑开始 NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: UIResponder.keyboardWillShowNotification, object: nil) // 同时注册键盘隐藏的通知,处理收起键盘时的约束恢复 NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name: UIResponder.keyboardWillHideNotification, object: nil) } // 控制器销毁时移除观察者,避免内存泄漏 deinit { NotificationCenter.default.removeObserver(self) } func textViewDidBeginEditing(_ textView: UITextView) { // 这里只需确保编辑状态正常激活即可 textView.becomeFirstResponder() } @objc func keyboardWillShow(notification: Notification) { guard let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue else { return } let keyboardHeight = keyboardSize.height if #available(iOS 11.0, *){ // iOS11+减去底部安全区域高度,避免视图被安全区域挡住 self.composeViewBottomConstraint.constant = keyboardHeight - view.safeAreaInsets.bottom } else { // iOS11以下直接使用键盘高度 self.composeViewBottomConstraint.constant = keyboardHeight } // 用动画包裹布局刷新,实现平滑上移 UIView.animate(withDuration: 0.3) { self.view.layoutIfNeeded() } } @objc func keyboardWillHide(notification: Notification) { // 收起键盘时恢复约束到初始值(通常为0) self.composeViewBottomConstraint.constant = 0 UIView.animate(withDuration: 0.3) { self.view.layoutIfNeeded() } } }
额外建议
- 尽量避免在用户交互事件(比如编辑开始)里注册通知,这类全局监听应该在控制器初始化/加载时完成,销毁时移除。
- 处理键盘通知时,同时监听
keyboardWillHide能确保收起键盘时视图恢复原位,提升整体用户体验。
内容的提问来源于stack exchange,提问作者badman
相关产品推荐
相关产品推荐

