为何UIView与键盘弹出同步动画不生效?(Xcode12纯代码)
问题:UIView随键盘弹出上移无动画效果,frame已更新但模拟器无变化
我尝试实现UIView随键盘弹出同步上移,确保输入框在键盘弹出时仍显示在可视区域。但代码中视图的frame已更新,模拟器却无动画效果。使用Xcode12纯代码开发(无Storyboard),该函数在键盘弹出通知触发时执行:
打印输出
showing keyboard begin (0.0, 528.96, 375.0, 88.16) end (0.0, 528.96, 375.0, 88.16) this should be changed (0.0, 237.96000000000004, 375.0, 379.15999999999997) end (0.0, 237.96000000000004, 375.0, 379.15999999999997)
原代码
@objc private func keyboardWillShow(notification: NSNotification) { print("showing keyboard") let userInfo: NSDictionary = notification.userInfo! as NSDictionary let keyboardFrame: NSValue = userInfo.value(forKey: UIResponder.keyboardFrameEndUserInfoKey) as! NSValue let keyboardRectangle = keyboardFrame.cgRectValue let keyboardHeight = keyboardRectangle.height let commentViewSize: CGFloat = view.height/7 print("begin") print(commentFieldView.frame) UIView.animate(withDuration: 0.1, delay: 0, animations: { [weak self] in guard let strongSelf = self else { return } DispatchQueue.main.async { strongSelf.commentFieldView.frame = CGRect(x: 0, y: strongSelf.view.height - commentViewSize - keyboardHeight, width: strongSelf.view.width, height: commentViewSize + keyboardHeight) strongSelf.profilePic.frame = CGRect(x: strongSelf.commentFieldView.left + 15, y: strongSelf.commentFieldView.height/5, width: 100, height: 100) print("this should be changed") print(strongSelf.commentFieldView.frame) } }) { _ in print("end") print(self.commentFieldView.frame) } print("end") print(self.commentFieldView.frame) }
解决方案
问题根源
- 动画上下文被破坏:
UIView.animate的闭包本身就在主队列执行,嵌套DispatchQueue.main.async会让frame更新操作脱离动画上下文,无法触发动画。 - 动画时长不合理:手动设置的0.1秒时长过短,几乎无法感知,建议使用键盘通知自带的动画时长,和系统键盘动画保持同步。
修复后的代码
@objc private func keyboardWillShow(notification: NSNotification) { print("showing keyboard") guard let userInfo = notification.userInfo else { return } // 获取键盘最终高度 let keyboardFrame = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as! NSValue let keyboardHeight = keyboardFrame.cgRectValue.height let commentViewSize: CGFloat = view.height/7 print("begin") print(commentFieldView.frame) // 获取系统键盘的动画时长 let animationDuration = userInfo[UIResponder.keyboardAnimationDurationUserInfoKey] as! TimeInterval UIView.animate(withDuration: animationDuration) { [weak self] in guard let strongSelf = self else { return } // 直接在动画闭包内更新frame,无需嵌套主队列 strongSelf.commentFieldView.frame = CGRect( x: 0, y: strongSelf.view.height - commentViewSize - keyboardHeight, width: strongSelf.view.width, height: commentViewSize + keyboardHeight ) strongSelf.profilePic.frame = CGRect( x: strongSelf.commentFieldView.left + 15, y: strongSelf.commentFieldView.height/5, width: 100, height: 100 ) print("this should be changed") print(strongSelf.commentFieldView.frame) } completion: { _ in print("end") print(self?.commentFieldView.frame ?? .zero) } print("end") print(self.commentFieldView.frame) }
额外提示
- 如果视图使用Auto Layout约束,直接修改frame可能和约束冲突,建议通过修改约束的
constant值实现上移动画,这是更稳妥的方案。 - 记得在
viewDidLoad中正确注册键盘通知:
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(notification:)), name: UIResponder.keyboardWillShowNotification, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(notification:)), name: UIResponder.keyboardWillHideNotification, object: nil)
内容的提问来源于stack exchange,提问作者Car Lord VR
相关产品推荐
相关产品推荐

