我目前正在我的应用程序中实现聊天。这是一个非常简单的tableview,显示了作为消息的行。我有一个很大的问题,想知道如何对不同的键盘做出反应。
目前我在计算键盘时计算键盘的高度,并将其添加为偏移量。我之前发过关于这个主题的问题,人们倾向于告诉我应该使用插图,但我无法使其工作。
我希望tableview的内容像iMessage或Whatsapp一样移动,消息在键盘上向上移动并返回。它适用于标准键盘,但一旦你切换到表情符号,它就会搞砸了。
我需要帮助才能找到解决方案,或者向我展示在tableviews中处理键盘的正确方法。
为了安全每个人的时间,我创建了一个虚拟项目,它保存了我当前的聊天状态。我希望有人之前已经实现了这一点并且可以帮助我,因为我现在急需找到解决方案。 https://www.dropbox.com/s/bldknydjfrwknr9/chatOffset.zip?dl=0
也许偏移的方式是错误的,有人可以告诉我一个更好的解决方案?
您应该实现此代码。请注意,这是在Swift 2.3中,但我已经在Swift 3上实现了这个代码并且它起作用我现在还没有Swift 3代码的副本。
// Tag your bottom Layout Constraint (probably the one at the bottom of your tableView which connects to the self.view or the one connected to your textField to the self.view)
@IBOutlet var bottomLayoutConstraint: NSLayoutConstraint!
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
// this code snippet will observe the showing of keyboard
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(self.keyboardWillShowNotification(_:)), name: UIKeyboardWillShowNotification, object: nil)
// this code snippet will observe the hiding of keyboard
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(self.keyboardWillHideNotification(_:)), name: UIKeyboardWillHideNotification, object: nil)
}
func keyboardWillShowNotification(notification: NSNotification) {
updateBottomLayoutConstraintWithNotification(notification)
}
func keyboardWillHideNotification(notification: NSNotification) {
updateBottomLayoutConstraintWithNotification(notification)
}
func updateBottomLayoutConstraintWithNotification(notification: NSNotification) {
let userInfo = notification.userInfo!
// get data from the userInfo
let animationDuration = (userInfo[UIKeyboardAnimationDurationUserInfoKey] as! NSNumber).doubleValue
let keyboardEndFrame = (userInfo[UIKeyboardFrameEndUserInfoKey] as! NSValue).CGRectValue()
let convertedKeyboardEndFrame = view.convertRect(keyboardEndFrame, fromView: view.window)
let rawAnimationCurve = (notification.userInfo![UIKeyboardAnimationCurveUserInfoKey] as! NSNumber).unsignedIntValue << 16
let animationCurve = UIViewAnimationOptions(rawValue: UInt(rawAnimationCurve))
bottomLayoutConstraint.constant = CGRectGetMaxY(view.bounds) - CGRectGetMinY(convertedKeyboardEndFrame)
// animate the changes
UIView.animateWithDuration(animationDuration, delay: 0.0, options: [.BeginFromCurrentState, animationCurve], animations: {
self.view.layoutIfNeeded()
}, completion: nil)
}
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
// remove the observers so the code won't be called all the time
NSNotificationCenter.defaultCenter().removeObserver(self, name: UIKeyboardWillShowNotification, object: nil)
NSNotificationCenter.defaultCenter().removeObserver(self, name: UIKeyboardWillHideNotification, object: nil)
}
感谢Zonily Jame
这是我在Xcode 9中将他的代码转换为Swift 3.2
@IBOutlet weak var bottomLayoutConstraint: NSLayoutConstraint!
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShowNotification(notification:)), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHideNotification(notification:)), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
// remove the observers so the code won't be called all the time
NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillShow, object: nil)
NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillHide, object: nil)
}
func keyboardWillShowNotification(notification: NSNotification) {
updateBottomLayoutConstraintWithNotification(notification:notification)
}
func keyboardWillHideNotification(notification: NSNotification) {
updateBottomLayoutConstraintWithNotification(notification:notification)
}
func updateBottomLayoutConstraintWithNotification(notification: NSNotification) {
let userInfo = notification.userInfo!
// get data from the userInfo
let animationDuration = (userInfo[UIKeyboardAnimationDurationUserInfoKey] as! NSNumber).doubleValue
let keyboardEndFrame = (userInfo[UIKeyboardFrameEndUserInfoKey] as! NSValue).cgRectValue
let convertedKeyboardEndFrame = view.convert(keyboardEndFrame, from: view.window)
let rawAnimationCurve = (notification.userInfo![UIKeyboardAnimationCurveUserInfoKey] as! NSNumber).uint32Value << 16
let animationCurve = UIViewAnimationOptions(rawValue: UInt(rawAnimationCurve))
bottomLayoutConstraint.constant = (view.bounds).maxY - (convertedKeyboardEndFrame).minY
// animate the changes
UIView.animate(withDuration: animationDuration, delay: 0.0, options: [.beginFromCurrentState, animationCurve], animations: {
self.view.layoutIfNeeded()
}, completion: nil)
}
我在Zonily Jame和iCyberPaul的回答中遇到了一个问题。在键盘解雇时,我的表格视图(嵌入在堆栈视图中,底部约束到安全区域底部)延伸到标签栏后面。此修改版本只需将底部约束常量设置为0将隐藏。此版本还提供了一个插件处理程序。
用法:
HandlesKeyboard
子类中采用UIViewController
协议,例如,extension MyViewController: HandlesKeyboard { }
bottomConstraintForKeyboardHandling
@IBOutlet
。startHandlingKeyboardChanges()
或viewDidLoad
中打电话给viewWillAppear
。stopHandlingKeyboardChanges()
以删除self
作为观察者并在deinit
或viewWillDisappear
中调用它。import UIKit
protocol HandlesKeyboard where Self: UIViewController {
var bottomConstraintForKeyboardHandling: NSLayoutConstraint! { get }
func startHandlingKeyboardChanges()
func stopHandlingKeyboardChanges()
}
extension HandlesKeyboard {
/// Registers caller to start handling keyboard will show and will hide notifications
/// - Warning: Caller *must* implement `stopHandlingKeyboardChanges()` to unregister, e.g., in `deinit`
/// ```
/// func stopHandlingKeyboardChanges() {
/// NotificationCenter.default.removeObserver(self)
/// }
///
/// deinit {
/// stopHandlingKeyboardChanges()
/// }
/// ```
func startHandlingKeyboardChanges() {
NotificationCenter.default.addObserver(forName: UIResponder.keyboardWillShowNotification, object: nil, queue: nil) { [weak self] in
self?.updateBottomConstraint(notification: $0)
}
NotificationCenter.default.addObserver(forName: UIResponder.keyboardWillHideNotification, object: nil, queue: nil) { [weak self] in
self?.updateBottomConstraint(notification: $0)
}
}
// Intentionally not implemented to ensure caller unregisters
// func stopHandlingKeyboardChanges() {
// NotificationCenter.default.removeObserver(self)
// }
func updateBottomConstraint(notification: Notification) {
guard let userInfo = notification.userInfo,
let animationDuration = (userInfo[UIResponder.keyboardAnimationDurationUserInfoKey] as? NSNumber)?.doubleValue,
let keyboardEndFrame = (userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue,
let rawAnimationCurve = (userInfo[UIResponder.keyboardAnimationCurveUserInfoKey] as? NSNumber)?.uint32Value
else { return }
if notification.name == UIResponder.keyboardWillHideNotification {
bottomConstraintForKeyboardHandling.constant = 0.0
} else {
bottomConstraintForKeyboardHandling.constant = view.bounds.maxY - view.convert(keyboardEndFrame, to: view.window).minY
}
let animationCurve = UIView.AnimationOptions(rawValue: UInt(rawAnimationCurve << 16))
UIView.animate(withDuration: animationDuration, delay: 0.0, options: [.beginFromCurrentState, animationCurve], animations: { [weak self] in
self?.view.layoutIfNeeded()
}, completion: nil)
}
}