最近在开发的过程中,有个需求就是自定义消息具体的需求看设计图效果,下方放出,并且记录一下开发过程,仅供学习参考
1.设计效果图展示
2.集成腾讯云SDK
具体的配置,我就不讲了,可以去看看官方文档,里面有详细介绍,
我的项目中主要是pod了下面的库:
#腾讯云即时通讯
pod 'TXIMSDK_TUIKit_iOS'
3.自定义消息其实没什么难度,就是创建一个继承至TUIMessageCell试图 把数据赋值上去
- 我的项目中需要自定义好多种,但是都是换汤不换药,会了一种其他的都简单,难点都不在这里,下面我贴出我的其中的一种自定义试图代码
class WMFinshWishCell: TUIMessageCell {
lazy var bgView:UIView = {
let view = UIView()
view.backgroundColor = UIColor.white
view.layer.cornerRadius = 5
view.layer.masksToBounds = true
return view
}()
lazy var titleLabel:YYLabel = {
let view = YYLabel()
view.font = WMFontBold(14)
view.textColor = color("#333333")
view.numberOfLines = 0
return view
}()
lazy var timeLabel:YYLabel = {
let view = YYLabel()
view.font = WMFont(11)
view.textColor = color("#999999")
return view
}()
lazy var wishButton:UIButton = {
let view = UIButton()
view.layer.cornerRadius = 3
view.layer.masksToBounds = true
view.setTitle("心愿", for: .normal)
view.setTitle("约会", for: .selected)
view.titleLabel?.font = WMFont(11.5)
view.setBackgroundImage(UIImage(color: UIColor(hexString: "#E8B916")!), for: .normal)
view.setBackgroundImage(UIImage(color: UIColor(hexString: "#FB5798")!), for: .selected)
return view
}()
lazy var shopView:shopDetialView = {
let view = shopDetialView()
view.backgroundColor = color("#EEEEEE")
view.layer.cornerRadius = 3
view.layer.masksToBounds = true
return view
}()
lazy var contentLabel:YYLabel = {
let view = YYLabel()
view.font = WMFont(10)
view.textColor = color("#333333")
view.numberOfLines = 0
return view
}()
lazy var tipLabel:YYLabel = {
let view = YYLabel()
view.font = WMFont(10)
view.textColor = color("#F14545")
view.numberOfLines = 0
return view
}()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
self.avatarView.isHidden = true
//Tip:这里如果需要显示头像就添加到containview上、否则添加到self上
addSubview(bgView)
bgView.addSubview(titleLabel)
bgView.addSubview(timeLabel)
bgView.addSubview(wishButton)
bgView.addSubview(shopView)
bgView.addSubview(contentLabel)
bgView.addSubview(tipLabel)
bgView.snp.makeConstraints { (make) in
make.left.equalTo(WMRatio(12))
make.top.equalToSuperview()
make.bottom.equalTo(-WMRatio(12))
make.width.equalTo(iphone.width - WMRatio(24))
}
}
override func fill(with data: TCommonCellData!) {
super.fill(with: data)
guard let data = data as? WMFinishWishCellData else {
return
}
titleLabel.text = data.model?.title?.stringValue
timeLabel.text = data.model?.time?.stringValue.transformTimeChuoToTime(dateFormat: "yyyy-MM-dd HH:mm")
wishButton.isSelected = data.model?.type == 1 ? false : true
shopView.shopImg.yy_setImage(with: URL(string: data.model?.shop_img?.stringValue ?? ""), placeholder: UIImage(named: ""))
shopView.shopTitle.text = data.model?.shop_title?.stringValue
shopView.priceLabel.text = data.model?.shop_price?.stringValue
contentLabel.text = data.model?.content
tipLabel.text = data.model?.tip?.stringValue
titleLabel.frame = data.titleFram
timeLabel.frame = data.timeFram
wishButton.frame = data.wishFram
shopView.frame = data.shopFram
contentLabel.frame = data.contentFram
tipLabel.frame = data.tipFram
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func awakeFromNib() {
super.awakeFromNib()
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
}
//MARK:设置商品详情试图
extension WMFinshWishCell {
class shopDetialView:UIView{
lazy var shopImg:UIImageView = {
let view = UIImageView()
return view
}()
lazy var shopTitle:UILabel = {
let view = UILabel()
view.font = WMFont(14)
view.textColor = color("#333333")
return view
}()
lazy var priceLabel:UILabel = {
let view = UILabel()
view.font = WMFont(14)
view.textColor = color("#F14545")
return view
}()
override init(frame: CGRect) {
super.init(frame: frame)
addSubview(shopImg)
addSubview(shopTitle)
addSubview(priceLabel)
shopImg.snp.makeConstraints { (make) in
make.left.top.bottom.equalToSuperview()
make.height.width.equalTo(WMRatio(50))
}
shopTitle.snp.makeConstraints { (make) in
make.left.equalTo(shopImg.snp.right).offset(WMRatio(8))
make.top.equalTo(shopImg.snp.top).offset(WMRatio(5))
make.right.equalToSuperview().offset(-WMRatio(12))
}
priceLabel.snp.makeConstraints { (make) in
make.left.equalTo(shopImg.snp.right).offset(WMRatio(8))
make.top.equalTo(shopTitle.snp.bottom).offset(8)
}
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
}
4.实现代理方法TUIChatControllerDelegate
- 主要是两个代理方法 一个是 func chatController(_ controller: TUIChatController!, onShowMessageData cellData: TUIMessageCellData!) -> TUIMessageCell! 另一个 func chatController(_ controller: TUIChatController!, onNewMessage msg: TIMMessage!) -> TUIMessageCellData! ,具体的代码实现在下面
//因为我的项目种自定义的消息太多,所以下面的代码中我删掉了一部分,和项目代码不一样,但实现方法都一样 ,在这个过程中 我 遇到了一个问题 就是 传数据的时候,因为有好多种类型,我需要传一个style,但是腾讯云外接的只有一个data,我把自己的model已经生成了data了,然后把它放在字典里,再转换成data,但是这样写会崩溃,报错说里面不是json,当然,你也可以把数据全部转换成json字符串写,也没问题,但是我懒得改了就直接用NSKeyedUnarchiver归档解档存取数据了,具体的看你们自己,想用什么用什么
func chatController(_ controller: TUIChatController!, onNewMessage msg: TIMMessage!) -> TUIMessageCellData! {
let elem = msg.getElem(0)
if (elem?.isKind(of: TIMCustomElem.self))!{
let customElem = elem as? TIMCustomElem
guard let dict = NSKeyedUnarchiver.unarchiveObject(with: customElem!.data) as? Dictionary<String, Any> else {
return nil
}
let style = Number(dict)["type"].intValue
let itemData = dict["model"] as! Data
if (style == 2) {
let cellData = WMFinishWishCellData(direction: msg.isSelf() ? TMsgDirection.MsgDirectionOutgoing : TMsgDirection.MsgDirectionIncoming)
let obj = try? JSONDecoder().decode(WMFinishWishCellData.finshwishModel.self, from: itemData)
cellData.model = obj
return cellData
}
else{
return nil
}
}
return nil
}
func chatController(_ controller: TUIChatController!, onShowMessageData cellData: TUIMessageCellData!) -> TUIMessageCell! {
if cellData.isKind(of: WMFinishWishCellData.self) {
let cell = WMFinshWishCell(style: .default, reuseIdentifier: "finshCell2")
cell.fill(with: cellData)
return cell
}
return nil
}
5.以上就已经实现了自定义消息 ,讲讲实现过程中遇到的问题
其实自定义消息没什么难点,但是我的项目中,我遇到了几个问题:
1.有些消息只可以自己看的到,对方看不到。
2.可以自动发送一条文本消息。
3.对方在我这边发送了一个赴约申请时,他那边需要收到的是同意拒绝消息。
4.删除上一条消息。
6.问题的解决方式
1.在你继承TUIMessageCellData的数据中多传一个isSend值判断是不是发送方是的话就做处理,这个在文章第4点第一个代理方法中判断 返回nil 数据不传出去
if obj?.isSend?.intValue == 0 {
return nil
}
2.发送一个通知,在源码中监听发送
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "发送默认消息"), object: nil, userInfo: ["text":"已帮你完成心愿"])
3.监听消息的接收,开始我用的是代理的方法func chatController(_ controller: TUIChatController!, onNewMessage msg: TIMMessage!) -> TUIMessageCellData!,但是发现有个问题就是第一次他会加载所有的聊天记录,这显然不是我想要的,所以我们监听源码中的接收消息的通知 根据内容做判断
//监听收到的信息
NotificationCenter.default.addObserver(self, selector: #selector(onReserveNewsMsg(notic:)), name: NSNotification.Name(rawValue: "TUIKitNotification_TIMMessageListener"), object: nil),实现监听方法
4.这里我从源码里面去写了一个删除数据的方法 外接出来 ,在TUIMessageController类中实现- (void)onDeleteOld:(TUIMessageCellData *)data方法,实现内容如下:
-(void)onDeleteOld:(TUIMessageCellData *)data
{
_menuUIMsg = data;
TIMMessage *imMsg = _menuUIMsg.innerMessage;
if(imMsg == nil){
return;
}
if([imMsg remove]){
[self.tableView beginUpdates];
NSInteger index = [_uiMsgs indexOfObject:_menuUIMsg];
[_uiMsgs removeObjectAtIndex:index];
[_heightCache removeObjectAtIndex:index];
[self.tableView deleteRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:index inSection:0]] withRowAnimation:UITableViewRowAnimationFade];
[self.tableView endUpdates];
}
}
7.总结
自定义消息没多大难度,看你们的需求,理解里面的执行步骤,一步一步来,先完善发送方,再来接收方,你就会发现,其实蛮简单!