一、创建ChatViewCell
首先需要创建根视图ChatViewCell,然后所有的MessageCell都继承ChatViewCell,这样方便以后拓展通用功能.
public class ChatViewCell: UITableViewCell {
public var message: Message?
}
二、创建ChatSystemCell
ChatSystemCell是系统消息,一个居中的UILabel,外围添加背景框,布局如下图:
![](https://img.haomeiwen.com/i1429831/44cf536628cab3c1.png)
//
// ChatSystemCell.swift
//
import UIKit
import Cartography
public class ChatSystemCell: ChatViewCell {
public let systemLabel = UILabel.init()
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
let bgView = UIView.init()
bgView.layer.cornerRadius = 5
bgView.backgroundColor = UIColor.colorFromRGB(0xebebf1)
bgView.addSubview(systemLabel)
self.contentView.addSubview(bgView)
systemLabel.numberOfLines = 0
systemLabel.font = UIFont.systemFont(ofSize: 16)
systemLabel.textAlignment = .center
constrain(systemLabel, bgView) { (systemLabel, bgView) in
systemLabel.height >= 10
systemLabel.edges == bgView.edges.inseted(top: 5, leading: 10, bottom: 5, trailing: 10)
bgView.centerX == bgView.superview!.centerX
bgView.top == bgView.superview!.top + 10
bgView.bottom == bgView.superview!.bottom - 10
bgView.left >= bgView.superview!.left + 50
bgView.right <= bgView.superview!.right - 50
}
}
required public init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override public var message: Message? {
didSet {
if let message = message {
systemLabel.text = message.comment
}
}
}
}
三、创建其他消息的父类ChatMessageCell
由于普通消息的头像和气泡会因为角色出现在左右两侧的情况,这就应该统一在ChatMessageCell中根据角色的不同统一处理,之后在子类里就无需关心头像和气泡了.
![](https://img.haomeiwen.com/i1429831/7946db5d79f60b19.png)
![](https://img.haomeiwen.com/i1429831/9d969b98a1888cb4.png)
1、在init里创建基本布局,将那些基本不会变的布局先设置好
2、在设置Message时,根据角色的不同,改变UI的布局.同时设置view的内容
public class ChatMessageCell: ChatViewCell {
private var messageView: UIView!
private var backgroundImageView: UIImageView!
public let headerView: UIImageView = UIImageView.init()
public let timeLabel: UILabel = UILabel.init()
public let idLabel: UILabel = UILabel.init()
public init(style: UITableViewCellStyle, reuseIdentifier: String?, messageView: UIView, backgroundImageView: UIImageView = UIImageView.init()) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setupLayout(messageView: messageView, backgroundImageView: backgroundImageView)
}
private func setupLayout(messageView: UIView, backgroundImageView: UIImageView) {
// 基本布局
}
public override var message: Message? {
didSet {
if let message = message {
let from = message.from
assert(from != .system, "类型不正确")
// 更新布局
if from == .me {
// 改变UI布局,左边内容右边头像
}else{
// 改变UI布局,右边内容左边头像
}
// 设置数据
}
}
}
}
这里有一点需求,就是图片消息需要做成像微信那样没有气泡的样子(如下图),因为没有背景图,所以我们需要将ChatMessageCell中的backgroundImageView设置成Optional的,由子类控制是否需要气泡背景.所以代码需要修改下
![](https://img.haomeiwen.com/i1429831/c64fd91167a4a631.png)
public class ChatMessageCell: ChatViewCell {
private var messageView: UIView!
private var backgroundImageView: UIImageView?
public let headerView: UIImageView = UIImageView.init()
public let timeLabel: UILabel = UILabel.init()
public let idLabel: UILabel = UILabel.init()
private var gestureRecognizerTuple: (UITapGestureRecognizer?, UILongPressGestureRecognizer?)
public var tapGestureBlock: ((UIGestureRecognizer) -> Void)? {
didSet {
let view = actionView
if tapGestureBlock != nil {
gestureRecognizerTuple.0 = UITapGestureRecognizer.init(target: self, action: #selector(tapAction(_:)))
view.addGestureRecognizer(gestureRecognizerTuple.0!)
}else{
view.removeGestureRecognizer(gestureRecognizerTuple.0!)
gestureRecognizerTuple.0 = nil
}
}
}
public var longPressGestureBlock: ((UILongPressGestureRecognizer) -> Void)? {
didSet {
let view = actionView
if longPressGestureBlock != nil {
gestureRecognizerTuple.1 = UILongPressGestureRecognizer.init(target: self, action: #selector(longPressAction(_:)))
view.addGestureRecognizer(gestureRecognizerTuple.1!)
}else{
view.removeGestureRecognizer(gestureRecognizerTuple.1!)
gestureRecognizerTuple.1 = nil
}
}
}
private var actionView: UIView {
var view: UIView!
if let imageView = backgroundImageView {
messageView.isUserInteractionEnabled = false
backgroundImageView?.isUserInteractionEnabled = true
view = imageView
}else{
messageView.isUserInteractionEnabled = true
view = messageView
}
return view
}
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
fatalError("should use init(style:reuseIdentifier:messageView:backgroundImageView:) initialize")
}
public init(style: UITableViewCellStyle, reuseIdentifier: String?, messageView: UIView, backgroundImageView: UIImageView? = UIImageView.init()) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setupLayout(messageView: messageView, backgroundImageView: backgroundImageView)
}
required public init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
@objc func longPressAction(_ longPress: UILongPressGestureRecognizer){
longPressGestureBlock!(longPress)
}
@objc func tapAction(_ tap: UITapGestureRecognizer) {
tapGestureBlock!(tap)
}
private func setupLayout(messageView: UIView, backgroundImageView: UIImageView?) {
self.messageView = messageView
self.backgroundImageView = backgroundImageView
timeLabel.font = UIFont.systemFont(ofSize: 14)
idLabel.textAlignment = .center
idLabel.adjustsFontSizeToFitWidth = true
self.contentView.addSubview(headerView)
self.contentView.addSubview(messageView)
self.contentView.addSubview(timeLabel)
self.contentView.addSubview(idLabel)
if let backgroundImageView = backgroundImageView {
self.backgroundImageView = backgroundImageView
self.contentView.insertSubview(backgroundImageView, belowSubview: messageView)
constrain(timeLabel, headerView, messageView, backgroundImageView, idLabel) { (timeLabel, headerView, messageView, backgroundImageView, idLabel) in
let superview = timeLabel.superview!
timeLabel.top == superview.top
timeLabel.centerX == superview.centerX
headerView.height == 44
headerView.width == 44
idLabel.edges == headerView.edges
headerView.top == backgroundImageView.top
// headerView left and right depend message.from
backgroundImageView.top == timeLabel.bottom + 10
backgroundImageView.bottom == superview.bottom - 10
// backgroundImageView leading and trailing depend message.from
messageView.height >= 24.5
messageView.width >= 5
}
} else {
constrain(timeLabel, headerView, messageView, idLabel) { (timeLabel, headerView, messageView, idLabel) in
let superview = timeLabel.superview!
timeLabel.top == superview.top
timeLabel.centerX == superview.centerX
headerView.height == 44
headerView.width == 44
idLabel.edges == headerView.edges
headerView.top == messageView.top
// headerView left and right depend message.from
messageView.top == timeLabel.bottom + 10
messageView.bottom == superview.bottom - 10
// backgroundImageView leading and trailing depend message.from
messageView.height >= 44
messageView.width >= 20
}
}
}
private let group = ConstraintGroup()
public override var message: Message? {
didSet {
if let message = message {
let from = message.from
assert(from != .system, "类型不正确")
// 更新布局
if let backgroundImageView = backgroundImageView {
constrain(headerView, messageView, backgroundImageView, replace: group) { (headerView, messageView, backgroundImageView) in
let superview = headerView.superview!
if from == .me {
headerView.right == superview.right - 20
backgroundImageView.edges == messageView.edges.inseted(top: -10, leading: -10, bottom: -10, trailing: -17)
backgroundImageView.left >= superview.left + 80
backgroundImageView.right == headerView.left - 3
}else{
headerView.left == superview.left + 20
backgroundImageView.edges == messageView.edges.inseted(top: -10, leading: -17, bottom: -10, trailing: -10)
backgroundImageView.right <= superview.right - 80
backgroundImageView.left == headerView.right + 3
}
}
} else {
constrain(headerView, messageView, replace: group) { (headerView, messageView) in
let superview = headerView.superview!
if from == .me {
headerView.right == superview.right - 20
messageView.left >= superview.left + 80
messageView.right == headerView.left - 10
}else{
headerView.left == superview.left + 20
messageView.right <= superview.right - 80
messageView.left == headerView.right + 10
}
}
}
// 设置数据
idLabel.text = "\(message.id ?? -1)"
timeLabel.text = message.isShowTime ? "\(Date.init(timeIntervalSinceReferenceDate:message.timestamp).chatString)" : nil
let headerName = message.from == .me ? "user_agent" : "user_ customer"
var image: UIImage? = imageCache().object(forKey: headerName as AnyObject) as? UIImage
if let image = image {
headerView.image = image
}else{
image = UIImage.init(named: headerName)?.ellipseImage()
headerView.image = image
DispatchQueue.global().async {
imageCache().setObject(image!, forKey: headerName as AnyObject)
}
}
if let backgroundImageView = backgroundImageView {
let bgName = message.from == .me ? "chat_messagebg_me" : "chat_messagebg_other"
backgroundImageView.image = UIImage.init(named: bgName)
backgroundImageView.highlightedImage = UIImage.init(named: "\(bgName)_pre")
}
}
}
}
}
上述代码中同时加入的单击和双击的事件处理,当子类需要这些事件时,只需要添加tapGestureBlock或longPressGestureBlock即可支持点击或双击功能.
上一篇:聊天功能(一)--Message模型
下一篇:聊天功能(三)--创建TextCell、ImageCell等
Demo地址:MAChatTableViewDemo
网友评论