聊天室键盘弹出&键盘收回&发送消息后更新对话列表

最近一个朋友做了一个类似于聊天室的界面, 但是遇到了发送消息后, 消息列表中并没有显示的问题, 刚开始也以为是数据源没有更新, 列表没有刷新等问题造成的, 但是在解决过程中发现并不是这么简单, 于是直接写了一个demo, 从头实现了一遍, 而本文主要是讲述其中两个事情: 键盘事件, 更新对话列表.

添加键盘事件通知

override func viewDidLoad() {
        super.viewDidLoad()
        
        NotificationCenter.default.addObserver(self, selector: #selector(keyBoardWillShow), name: .UIKeyboardWillShow, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(keyBoardWillDiss), name: .UIKeyboardWillHide, object: nil)
    }

收到通知, 触发方法:

注: 通过修改输入框所在的View与底部Safe Area.Bottom的距离实现页面与键盘的布局变化.

// MARK: - 通知
extension ChatRoomVC {
    
    @objc func keyBoardWillShow(notification: NSNotification) {
        let keyBoardCGRect = notification.userInfo?["UIKeyboardFrameEndUserInfoKey"] as? CGRect
        let height = keyBoardCGRect?.height ?? 0
        let duration = notification.userInfo?["UIKeyboardAnimationDurationUserInfoKey"] as? CGFloat
        
        UIView.animate(withDuration: TimeInterval(duration ?? 0)) { [weak self] in
            self?.bottomLayoutConstraint.constant = -height
            self?.view.layoutIfNeeded()
        }
        
        tableView.scrollToRow(at: IndexPath(row: dataSourceArray.count - 1, section: 0), at: .bottom, animated: false)
    }
    
    @objc func keyBoardWillDiss(notification: NSNotification) {

        let duration = notification.userInfo?["UIKeyboardAnimationDurationUserInfoKey"] as? CGFloat
        UIView.animate(withDuration: TimeInterval(duration ?? 0)) { [weak self] in
            self?.bottomLayoutConstraint.constant = 0
            self?.view.layoutIfNeeded()
        }
        
        tableView.scrollToRow(at: IndexPath(row: dataSourceArray.count - 1, section: 0), at: .bottom, animated: false)
}

处理消息发送, 更新对话列表

注: 这里只是简单的直接修改了本地数据集合, 而实际开发中, 需要通过socket连接, 来完成数据的同步工作.

// MARK: - UITextFieldDelegate
extension ChatRoomVC: UITextFieldDelegate {
    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        dataSourceArray.append(textField.text ?? "")
        let indexPath = IndexPath(row: dataSourceArray.count - 1, section: 0)
        tableView.insertRows(at: [indexPath], with: .bottom)
        tableView.scrollToRow(at: indexPath, at: .bottom, animated: true)
        textField.text = nil
        return true
    }
}

以下是两个注意点: 当滑动列表或者点击了列表时, 需要回收键盘.

滑动列表时

// MARK: - UIScrollViewDelegate
extension ChatRoomVC: UIScrollViewDelegate {
    func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
        textField.endEditing(true)
    }
}

点击列表, 这里面有一个问题, 就是UIScrollView, UITableView, UICollectionView是不响应touch事件的, 也就是说只是简单的重写touch的方法是没有用的, 方法不会触发, 如果想让其响应touch事件, 需要下面的处理方法:

//MARK : - 处理UIScrollView类型不响应touch事件的问题
extension UITableView {
    open override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
        let view = super.hitTest(point, with: event)
        self.viewController()?.view.endEditing(true)
        if view != nil && view!.isKind(of: UITableView.self) {
            return self
        }else {
            return view
        }
    }
}

// 重写touch触发的方法
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        textField.endEditing(true)
    }

推荐阅读更多精彩内容