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

问题描述 投票:1回答:3

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

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

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

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

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

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

ios swift uitableview keyboard chat
3个回答
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)
}

2
投票

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

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)
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.