美文网首页人猿星球iOS 进阶
iOS直播间聊天室—图文混排加载网络图片(含Demo)

iOS直播间聊天室—图文混排加载网络图片(含Demo)

作者: 大怪猿 | 来源:发表于2019-03-29 16:57 被阅读469次

    Question:

    1.刷新直播间消息机制该用哪种方法?哪一种更加合适?

    2.聊天室该如何图文混排?

    3.聊天室出现特殊字符临界点不换行?高度计算错误?

    4.聊天室该如何加载网络图片?

    5.聊天室如何优化?

    6.交互时刷新消息经常出现越界情况,导致崩溃?

    7.聊天室出现阿拉伯文&中文&数字&英文等类似情况如何处理?

    ...

    以上这些问题我相信做过聊天室直播间的肯定或多或少遇到过,那么本编文章就为了解决这些问题而来,底部提供Demo⬇️。

    1.刷新直播间消息机制该用哪种方法?哪一种更加合适?

    聊天室刷新分为两种:
    一:采用来一条消息刷新一条消息,适用于小主播和人气较少的直播间。优点就是可以快速的查看到发言记录,给人丝滑流畅感,缺点就是刷新过于频繁,消耗性能。


    image.png 直接刷新.gif

    二:定时刷新,适用于大主播或某段活动时间内用户发言特别频繁时间段,假设一秒钟接收到几十条消息时你直接一条条刷新是毫无意义的,不仅影响性能而且用户也看不清。所以这种情况可以选择弄个定时器,每0.5秒刷新一次。至于多久刷新一次,你可以根据主播人气,观看人数,发言速度来调整。


    image.png 0.5秒刷新一次.gif

    通过对比,二者都是每秒接收到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。


    image.png

    二:所有的消息所需要的背景颜色,富文本,高度等等都用模型记录。


    image.png

    三: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

    相关文章

      网友评论

        本文标题:iOS直播间聊天室—图文混排加载网络图片(含Demo)

        本文链接:https://www.haomeiwen.com/subject/ivmrvqtx.html