上一篇将所有需要使用的Cell都已经创建成功.接下来编写ChatViewController和ChatTableView.
一、ChatTableView.swift
ChatTableView只是简单的将继承DataSource,并使用FDTemplateLayoutCell自适应Cell的高度。关于Reloadable,会在下一篇文章中介绍。
import UIKit
import UITableView_FDTemplateLayoutCell
extension UITableView {
func register(identifier: String){
self.register(UINib.init(nibName: identifier, bundle: nil), forCellReuseIdentifier: identifier)
}
}
class ChatTableView: UITableView, Reloadable {
var reloadTableView: UITableView {return self}
var models: [Message] = []
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.dataSource = self
registerTableViewCell()
}
func registerTableViewCell(){
self.register(ChatTextCell.self, forCellReuseIdentifier: ChatTextCell.identifier)
self.register(ChatImageCell.self, forCellReuseIdentifier: ChatImageCell.identifier)
self.register(ChatVoiceCell.self, forCellReuseIdentifier: ChatVoiceCell.identifier)
self.register(ChatSystemCell.self, forCellReuseIdentifier: ChatSystemCell.identifier)
}
struct xixi { static var isScrollBottom: Bool = false }
deinit {
xixi.isScrollBottom = false
}
}
extension ChatTableView: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
//只在初始化的时候执行
DispatchQueue.once(&xixi.isScrollBottom, {
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.005) {
if self.models.count > 0 {
tableView.scrollToRow(at: IndexPath(row: self.models.count - 1, section: 0), at: .bottom, animated: false)
}
}
})
return models.count
}
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
let message = models[indexPath.row]
var height: CGFloat = 0
height = tableView.fd_heightForCell(withIdentifier: message.type.identifier, cacheByKey: "\(message.timestamp)" as NSCopying, configuration: { (cell) in
(cell as! ChatViewCell).message = message
})
return ceil(height)
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let message = models[indexPath.row]
let cell: ChatViewCell = tableView.dequeueReusableCell(withIdentifier: message.type.identifier) as! ChatViewCell
cell.selectionStyle = .none
cell.message = message
return cell
}
}
二、ChatViewController.swift
1、创建随机消息添加到屏幕上
创建一个定时器,每0.1秒添加一个Message至缓存池,缓存池会根据设置的时间定时将Message数组添加到ChatTableView上。
2、键盘弹出的处理
一般聊天界面,会有聊天框和聊天内容,关于键盘的显示和消失时的处理参考聊天界面键盘隐藏时的一个动画小问题的方式处理,当键盘弹出时,不改变ChatTableView的大小,将其向上平移,就避免了ChatTableView的那个奇怪的动画。
3、ChatTableView添加Message的刷新加载
关于Reloadable.refresh(_ models: [(Self.Model, MAExtension.RefreshMode)])
下篇文章讲解。
import UIKit
class ChatViewController: UIViewController {
@IBOutlet weak var tableView: ChatTableView!
@IBOutlet weak var toolView: UIView!
@IBOutlet weak var toolViewBottomLayout: NSLayoutConstraint!
@IBOutlet weak var tableViewTopLayout: NSLayoutConstraint!
var messagePool: MessagePool<(Message, RefreshMode)>!
// 是否添加消息
var isAddMessage = true
var timer: Timer?
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.delegate = self
startObserve()
// 定时添加消息
timer = Timer.timer(interval: 0.1, repeats: true, block: {[weak self] (timer) in
if let weakSelf = self {
if weakSelf.isAddMessage == true {
weakSelf.add(messages: [Message.randomMessage()])
}
}
})
// 设置缓存池
messagePool = MessagePool.init(interval: 0.5, maxPop: 50) {[unowned self] (messages) in
if messages.count > 0 {
self.tableView.refresh(messages)
}
}
}
// 添加消息
func add(messages: [Message]){
if messages.count == 0 { return }
messagePool.push(messages.map{($0, .insert(.bottom))})
}
@IBAction func refresh(_ sender: UIBarButtonItem) {
self.isAddMessage = !self.isAddMessage
sender.title = self.isAddMessage ? "auto" : "stop"
}
@IBAction func addMessages(_ sender: UIBarButtonItem) {
add(messages: [Message.randomMessage()])
}
@IBAction func remove(_ sender: UIBarButtonItem) {
Message.sortIndex = -1
self.tableView.models = []
self.tableView.reloadData()
}
deinit {
self.timer?.invalidate()
self.timer = nil
cleanCache()
NotificationCenter.default.removeObserver(self)
}
}
// MARK: - 键盘的监听
extension ChatViewController {
func startObserve(){
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(notify:)), name: Notification.Name.UIKeyboardWillShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(notify:)), name: Notification.Name.UIKeyboardWillHide, object: nil)
}
// 键盘将要出现
@objc func keyboardWillShow(notify: NSNotification){
let top = self.tableView.frame.origin.y
UIView.animate(withDuration: notify.userInfo![UIKeyboardAnimationDurationUserInfoKey] as! Double, delay: 0, options: UIViewAnimationOptions.init(rawValue: notify.userInfo![UIKeyboardAnimationCurveUserInfoKey] as! UInt), animations: {
let height = (notify.userInfo![UIKeyboardFrameEndUserInfoKey] as! NSValue).cgRectValue.size.height
self.toolViewBottomLayout.constant = height
self.tableViewTopLayout.constant = -height
self.view.layoutIfNeeded()
// 将界面的上消息较少时,需要特殊处理调整contentInset
if(self.tableView.contentSize.height - self.tableView.bounds.size.height < 0){
self.tableView.contentInset = UIEdgeInsetsMake(height-top, 0, 0, 0);
}else{
self.tableView.scrollBottom()
}
}, completion: nil)
}
// 键盘将要消失
@objc func keyboardWillHide(notify: NSNotification){
UIView.animate(withDuration: notify.userInfo![UIKeyboardAnimationDurationUserInfoKey] as! Double, delay: 0, options: UIViewAnimationOptions.init(rawValue: notify.userInfo![UIKeyboardAnimationCurveUserInfoKey] as! UInt), animations: {
self.toolViewBottomLayout.constant = 0
self.tableViewTopLayout.constant = 0
self.view.layoutIfNeeded()
self.tableView.contentInset = UIEdgeInsetsMake(0, 0, 0, 0);
}, completion: nil)
}
}
extension ChatViewController: UITableViewDelegate {
func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
self.toolView.endEditing(true)
}
}
上一篇:聊天功能(三)--创建TextCell、ImageCell等
下一篇:聊天功能(五)--ChatTableView的刷新能力Reloadable
Demo地址:MAChatTableViewDemo
网友评论