iOS Tableview控制器键盘偏移(示例项目内部)

时间:2017-04-10 20:22:43

标签: ios swift uitableview keyboard chat

我目前正在我的应用中实现聊天。这是一个非常简单的tableview,显示了作为消息的行。我有一个很大的问题,想知道如何对不同的键盘做出反应。

目前我在计算键盘时计算键盘的高度并将其添加为偏移量。我之前发过关于这个主题的问题,人们倾向于告诉我应该使用插图,但我无法使其工作。

我希望tableview的内容像iMessage或Whatsapp一样移动,消息在键盘上向上移动并返回。它适用于标准键盘,但一旦你切换到表情符号,它就会搞砸了。

我需要帮助才能找到解决方案,或者向我展示在tableviews中处理键盘的正确方法。

为了安全每个人的时间我创建了一个虚拟项目,它保存了我当前的聊天状态。我希望有人之前已经实现了这一点并且可以帮助我,因为我现在急需找到解决方案。 https://www.dropbox.com/s/bldknydjfrwknr9/chatOffset.zip?dl=0

也许偏移的方式是错误的,有人可以给我一个更好的解决方案吗?

3 个答案:

答案 0 :(得分:3)

您应该实现此代码。请注意,这是在 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)
}

答案 1 :(得分:2)

感谢Zonily Jame

这是我在Xcode 9中将他的代码转换为Swift 3.2

{{1}}

答案 2 :(得分:0)

我遇到一个Zonily JameiCyberPaul的答案的问题。在关闭键盘时,我的表格视图(嵌入堆栈视图,底部约束到安全区域底部)在选项卡栏后面延伸。此修改版本仅将底部约束常量设置为0即可隐藏。此版本还提供了一个嵌入式处理程序。

用法:

  1. 声明在HandlesKeyboard子类(例如UIViewController)中使用extension MyViewController: HandlesKeyboard { }协议
  2. 为最底部的视图/容器创建bottomConstraintForKeyboardHandling @IBOutlet
  3. 致电startHandlingKeyboardChanges()viewDidLoad中的viewWillAppear
  4. 实施stopHandlingKeyboardChanges(),以将self删除为观察者,并在deinitviewWillDisappear中对其进行调用。

View/download GitHub gist

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)
    }
}