【问题标题】:iOS Tableview Controller Keyboard offset (example project inside)iOS Tableview 控制器键盘偏移(示例项目里面)
【发布时间】:2017-09-06 00:52:59
【问题描述】:

我目前正在我的应用中实现聊天。这是一个非常简单的表格视图,显示作为消息的行。我很难弄清楚如何对不同的键盘做出反应。

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

我希望 tableview 的内容像在 iMessage 或 Whatsapp 中一样移动,其中消息通过键盘向上移动并返回。它确实适用于标准键盘,但一旦你切换到表情符号,它就会变得一团糟。

我需要帮助来找到解决方案,或者向我展示在表格视图中处理键盘的正确方法。

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

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

【问题讨论】:

  • 通常 tableview 不是创建聊天视图时的最佳方式。为了不花费大量时间来尝试找出键盘行为,我建议使用 JSQMessagesViewController 之类的库。很容易实现,既有Objective-C也有Swift实现,看起来像iMessages,也可以改。
  • 但它不支持 Swift 3 这就是问题所在。如果不是 tableview,应该如何构建 chatview?
  • 它是用 Objective-C 制作的,是的,但它确实支持 swift 3 look at this example in swift。我目前正在使用 2 应用程序和 swift 3。关于您的其他问题,我遇到的大多数实现都是 UICollectionView 实现,即使是上面的示例,实际上也是 CollectionView 实现

标签: ios swift uitableview keyboard chat


【解决方案1】:

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

    这是我在 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)
        }
    

    【讨论】:

      【解决方案3】:

      我在Zonily JameiCyberPaul 的回答中遇到了一个问题。在键盘关闭时,我的表格视图(嵌入在具有底部约束到安全区域底部的堆栈视图中)在选项卡栏后面延伸。这个修改后的版本只是简单地将底部约束常量设置为 0 on will hide。此版本还提供了一个插入式处理程序。

      用法:

      1. UIViewController 子类中声明采用HandlesKeyboard 协议,例如extension MyViewController: HandlesKeyboard { }
      2. 为最底部的视图/容器创建 bottomConstraintForKeyboardHandling @IBOutlet
      3. viewDidLoadviewWillAppear 中致电startHandlingKeyboardChanges()
      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)
          }
      }
      

      【讨论】:

        猜你喜欢
        • 2020-08-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多