Question:
1.刷新直播间消息机制该用哪种方法?哪一种更加合适?
2.聊天室该如何图文混排?
3.聊天室出现特殊字符临界点不换行?高度计算错误?
4.聊天室该如何加载网络图片?
5.聊天室如何优化?
6.交互时刷新消息经常出现越界情况,导致崩溃?
7.聊天室出现阿拉伯文&中文&数字&英文等类似情况如何处理?
...
以上这些问题我相信做过聊天室直播间的肯定或多或少遇到过,那么本编文章就为了解决这些问题而来,底部提供Demo⬇️。
1.刷新直播间消息机制该用哪种方法?哪一种更加合适?
聊天室刷新分为两种:
一:采用来一条消息刷新一条消息,适用于小主播和人气较少的直播间。优点就是可以快速的查看到发言记录,给人丝滑流畅感,缺点就是刷新过于频繁,消耗性能。
二:定时刷新,适用于大主播或某段活动时间内用户发言特别频繁时间段,假设一秒钟接收到几十条消息时你直接一条条刷新是毫无意义的,不仅影响性能而且用户也看不清。所以这种情况可以选择弄个定时器,每0.5秒刷新一次。至于多久刷新一次,你可以根据主播人气,观看人数,发言速度来调整。
通过对比,二者都是每秒接收到30条消息时,一条条刷新和定时刷新所消耗的性能非常明显。
如何选择,各位自行斟酌。
2.聊天室该如何图文混排?
如果在子线程生成我们的富文本,这个时候你肯定不可以在子线程创建UIimageView这些,然后转UIimage,我个人有几个技巧:
一:聊天肯定会带有等级系列,拿我的直播来说,现在有0-100级,我个人写了一个等级生成器,通过业务生成每个等级段所对应文字+图片+等级+渐变色形成view,然后我们启动APP时,通过view转image,内存就会保存有0-100张图片,所占300k左右而已。那么下次用的时候,直接传入等级获取对应的UIimage。
+ (instancetype)sharedInstance {
static EWLevelManager *instance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[EWLevelManager alloc] init];
instance.data = [NSMutableDictionary dictionary];
});
return instance;
}
- (void)setup {
[self.data removeAllObjects];
for (NSInteger i = 0; i <= 100; i++) {
// NDLeveBgView就是我的等级生成器,返回view
// 启动app我们调用一次这个方法,然后内存就有生成0-100等级图片
NDLeveBgView *view = [[NDLeveBgView alloc] init];
view.frame = CGRectMake(0, 0, 30.0, 14.0);
view.layer.cornerRadius = 2;
view.layer.masksToBounds = YES;
view.isShadeLv = YES;
view.level = i;
[self.data setObject:[self convertCreateImageWithUIView:view] forKey:[NSString stringWithFormat:@"%li", (long)i]];
}
// NSMutableData *data = [[NSMutableData alloc]init];
// NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc]initForWritingWithMutableData:data];
// [archiver encodeObject:self.data forKey:@"talkData"];
// [archiver finishEncoding];
// NSLog(@"查看byte = %lu", (unsigned long)data.length);
}
- (UIImage *)imageForLevel:(NSInteger)Level {
return [self.data objectForKey:[NSString stringWithFormat:@"%li", (long)Level]];
}
/** 将 UIView 转换成 UIImage */
- (UIImage *)convertCreateImageWithUIView:(UIView *)view {
//UIGraphicsBeginImageContext(view.bounds.size);
UIGraphicsBeginImageContextWithOptions(view.bounds.size, NO, [UIScreen mainScreen].scale);
CGContextRef ctx = UIGraphicsGetCurrentContext();
[view.layer renderInContext:ctx];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
至于图片如何转富文本我就不多赘述了,这种没啥可说的,都在demo中。
3.聊天室出现特殊字符临界点不换行?高度计算错误?
我想肯定有人遇到过这个问题,当我们使用UILabel时,根据消息生成的富文本,高度计算得明明很正确,但就是不换行!!当我们在测试的时候明明运行一切都正常,但是到了线上就出现不换行,高度计算错误等等系列问题。
我可以很明确的告诉大家,这就是UILabel的问题,他在不同系统版本表现不一样,经过我多次测试,曾经一度让我怀疑是不是我的问题,但是后来我换了YYLabel后,一切都清净了,所以抛弃UILabel吧,它在表达复杂的文本时,总是不那么如意。
跟我说一句:YYKit牛逼🐂!!!
可能有同学反驳,其实我也想证明是我的问题,如果有同学用UILabel做的直播间聊天室,希望不吝赐教。
4.聊天室该如何加载网络图片?
这个问题其实也好解决。
当我们生成礼物消息富文本时,先用礼物缩略的占位图替代,同时使用SD下载该图片,下载完了以后重新生成该富文本,接着通过代理回调去刷新该cell。
case NDSubMsgType_Gift_Text: { // 礼物弹幕(文本)消息
// 下载标签图片
[self downloadTagImage];
// 下载礼物图片
[self downloadGiftImage];
self.bgColor = NormalBgColor;
[self Gift_Text];
}
break;
/** 下载礼物缩略图 */
- (void)downloadGiftImage {
NSString *urlStr = self.msgModel.giftModel.thumbnailUrl;
if (!urlStr || urlStr.length < 1) {
return;
}
if (self.finishDownloadGiftImg) {
return;
}
self.finishDownloadGiftImg = YES;
// 1. 如果本地有图片
self.giftImage = [self cacheImage:urlStr];
if (self.giftImage) {
return;
}
// 2. 下载远程图片
NSURL *url = [NSURL URLWithString:urlStr];
EWWeakSelf
id sdLoad = [[SDWebImageManager sharedManager] loadImageWithURL:url options:0 progress:^(NSInteger receivedSize, NSInteger expectedSize, NSURL * _Nullable targetURL) {
} completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, SDImageCacheType cacheType, BOOL finished, NSURL * _Nullable imageURL) {
if (image){
// 刷新UI
weakSelf.giftImage = image;
// 更新属性文字
[weakSelf downloadTagImageFinish];
}
}];
[self.tempLoads addObject:sdLoad];
}
- (void)downloadTagImageFinish {
// 更新属性文字
[self msgUpdateAttribute];
// 通知代理刷新属性文字
if (self.delegate && [self.delegate respondsToSelector:@selector(attributeUpdated:)]) {
[self.delegate attributeUpdated:self];
}
}
/** 消息属性文字发生变化(更新对应cell) */
- (void)msgAttrbuiteUpdated:(NDMsgModel *)msgModel {
NSInteger row = [self.msgArray indexOfObject:msgModel];
if (row >= 0) {
[self.tableView reloadRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:row inSection:0]] withRowAnimation:UITableViewRowAnimationNone];
if (row == self.msgArray.count - 1) {
[self scrollToBottom:YES];
}
}
}
以上代码都是关键部分,更详细可以对照demo。
5.聊天室如何优化?
聊天室的优化其实也就是UITableView的优化:
一:我们所有的cell通过复用,每一种消息类型对应不同cell。
二:所有的消息所需要的背景颜色,富文本,高度等等都用模型记录。
三:cell高度缓存,每次都从我们的模型读取,更加高效快捷。
6.交互时刷新消息经常出现越界情况,导致崩溃?
崩溃的原因大多数都在同时对同一个数组操作、插入indexPaths出现问题等。
一:如何保证同一时间数组只执行一种操作?
加锁
锁有好多种,有自旋锁、信号量、递归锁、互斥锁等等
自旋锁性能最高,但是经过苹果确认是有问题存在的,所以你可以选择其他类型。
这里我选择的是互斥锁:
#pragma mark - 消息追加
- (void)addNewMsg:(NDMsgModel *)msgModel {
if (!msgModel) return;
pthread_mutex_lock(&_mutex);
// 消息不直接加入到数据源
[self.tempMsgArray addObject:msgModel];
pthread_mutex_unlock(&_mutex);
if (_reloadType == NDReloadLiveMsgRoom_Direct) {
[self tryToappendAndScrollToBottom];
}
}
/** 追加数据源 */
- (void)appendAndScrollToBottom {
if (self.tempMsgArray.count < 1) {
return;
}
pthread_mutex_lock(&_mutex);
// 执行插入
.....代码块
pthread_mutex_unlock(&_mutex);
...代码块
//清空消息重置
- (void)reset {
pthread_mutex_lock(&_mutex);
...代码块
pthread_mutex_unlock(&_mutex);
}
}
更多详见demo。
二:插入indexPaths出现越界问题,其实这个问题的产生也是因为我们对数组同时操作而导致的,如果你解决了数组的问题,那么这个问题也迎刃而解。
7.聊天室出现阿拉伯文&中文&数字&英文等类似情况如何处理?
这个问题非常有意思!具体可参考这篇文章
因为阿拉伯文、希腊文等系列语言是强语言,并且从右向左排列,而我们的中文也属于强语言,但是从左向右排列。当这二者碰撞到一起会怎么样呢?
到底是遵从我们的规则还是遵从阿拉伯文的规则?
我这里做的是强制按照中文规则排版。
// 强制排版(从左到右)
attribute.yy_baseWritingDirection = NSWritingDirectionLeftToRight;
attribute.yy_writingDirection = @[@(NSWritingDirectionLeftToRight | NSWritingDirectionOverride)];
// 强制排版(从左到右)
paraStyle.alignment = NSTextAlignmentLeft;
paraStyle.baseWritingDirection = NSWritingDirectionLeftToRight;
如果大家觉得有什么问题的,可以评论指出。
最后就是我们的demo了,如果觉得可以的希望点个star哦,Github