Commit 5565674b2fafddac2a0586a1a8e9d0afcc06f0a6

Authored by 张旭
1 parent 24a0a2ac

添加聊天室UI

LiveVideoCloud/LiveVideoCloud.xcodeproj/project.pbxproj
... ... @@ -132,6 +132,10 @@
132 132 D22830FF1E77C69E00627701 /* GiftListView.m in Sources */ = {isa = PBXBuildFile; fileRef = D22830FE1E77C69E00627701 /* GiftListView.m */; };
133 133 D22831091E77D8D500627701 /* GiftModel.m in Sources */ = {isa = PBXBuildFile; fileRef = D22831081E77D8D500627701 /* GiftModel.m */; };
134 134 D236967E1E7F9E4100E4BF71 /* GiftPayButton.m in Sources */ = {isa = PBXBuildFile; fileRef = D236967D1E7F9E4100E4BF71 /* GiftPayButton.m */; };
  135 + D23696841E7FC6A900E4BF71 /* chatListCell.m in Sources */ = {isa = PBXBuildFile; fileRef = D23696831E7FC6A900E4BF71 /* chatListCell.m */; };
  136 + D236968D1E7FC7C200E4BF71 /* CNBarrageManager.m in Sources */ = {isa = PBXBuildFile; fileRef = D236968A1E7FC7C200E4BF71 /* CNBarrageManager.m */; };
  137 + D236968E1E7FC7C200E4BF71 /* CNBarrageView.m in Sources */ = {isa = PBXBuildFile; fileRef = D236968C1E7FC7C200E4BF71 /* CNBarrageView.m */; };
  138 + D23696921E7FC83800E4BF71 /* chatMsgModel.m in Sources */ = {isa = PBXBuildFile; fileRef = D23696911E7FC83800E4BF71 /* chatMsgModel.m */; };
135 139 D25530001E7909AC00DF38A5 /* GiftAnimOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = D2552FFF1E7909AC00DF38A5 /* GiftAnimOperation.m */; };
136 140 D25530031E79115100DF38A5 /* AnimQueueManager.m in Sources */ = {isa = PBXBuildFile; fileRef = D25530021E79115100DF38A5 /* AnimQueueManager.m */; };
137 141 D25530061E7911A800DF38A5 /* GiftAnimationAction.m in Sources */ = {isa = PBXBuildFile; fileRef = D25530051E7911A800DF38A5 /* GiftAnimationAction.m */; };
... ... @@ -454,6 +458,14 @@
454 458 D22831081E77D8D500627701 /* GiftModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GiftModel.m; sourceTree = "<group>"; };
455 459 D236967C1E7F9E4100E4BF71 /* GiftPayButton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GiftPayButton.h; sourceTree = "<group>"; };
456 460 D236967D1E7F9E4100E4BF71 /* GiftPayButton.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GiftPayButton.m; sourceTree = "<group>"; };
  461 + D23696821E7FC6A900E4BF71 /* chatListCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = chatListCell.h; sourceTree = "<group>"; };
  462 + D23696831E7FC6A900E4BF71 /* chatListCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = chatListCell.m; sourceTree = "<group>"; };
  463 + D23696891E7FC7C200E4BF71 /* CNBarrageManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CNBarrageManager.h; sourceTree = "<group>"; };
  464 + D236968A1E7FC7C200E4BF71 /* CNBarrageManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CNBarrageManager.m; sourceTree = "<group>"; };
  465 + D236968B1E7FC7C200E4BF71 /* CNBarrageView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CNBarrageView.h; sourceTree = "<group>"; };
  466 + D236968C1E7FC7C200E4BF71 /* CNBarrageView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CNBarrageView.m; sourceTree = "<group>"; };
  467 + D23696901E7FC83800E4BF71 /* chatMsgModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = chatMsgModel.h; sourceTree = "<group>"; };
  468 + D23696911E7FC83800E4BF71 /* chatMsgModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = chatMsgModel.m; sourceTree = "<group>"; };
457 469 D2552FFE1E7909AC00DF38A5 /* GiftAnimOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GiftAnimOperation.h; sourceTree = "<group>"; };
458 470 D2552FFF1E7909AC00DF38A5 /* GiftAnimOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GiftAnimOperation.m; sourceTree = "<group>"; };
459 471 D25530011E79115100DF38A5 /* AnimQueueManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AnimQueueManager.h; sourceTree = "<group>"; };
... ... @@ -1225,6 +1237,8 @@
1225 1237 D22830F61E77C26C00627701 /* Chat */ = {
1226 1238 isa = PBXGroup;
1227 1239 children = (
  1240 + D23696881E7FC7C200E4BF71 /* Barrage */,
  1241 + D236968F1E7FC83800E4BF71 /* ChatMsgModel */,
1228 1242 D22830F71E77C27A00627701 /* ChatViews */,
1229 1243 );
1230 1244 path = Chat;
... ... @@ -1233,6 +1247,8 @@
1233 1247 D22830F71E77C27A00627701 /* ChatViews */ = {
1234 1248 isa = PBXGroup;
1235 1249 children = (
  1250 + D23696821E7FC6A900E4BF71 /* chatListCell.h */,
  1251 + D23696831E7FC6A900E4BF71 /* chatListCell.m */,
1236 1252 );
1237 1253 path = ChatViews;
1238 1254 sourceTree = "<group>";
... ... @@ -1262,6 +1278,26 @@
1262 1278 path = GiftModel;
1263 1279 sourceTree = "<group>";
1264 1280 };
  1281 + D23696881E7FC7C200E4BF71 /* Barrage */ = {
  1282 + isa = PBXGroup;
  1283 + children = (
  1284 + D23696891E7FC7C200E4BF71 /* CNBarrageManager.h */,
  1285 + D236968A1E7FC7C200E4BF71 /* CNBarrageManager.m */,
  1286 + D236968B1E7FC7C200E4BF71 /* CNBarrageView.h */,
  1287 + D236968C1E7FC7C200E4BF71 /* CNBarrageView.m */,
  1288 + );
  1289 + path = Barrage;
  1290 + sourceTree = "<group>";
  1291 + };
  1292 + D236968F1E7FC83800E4BF71 /* ChatMsgModel */ = {
  1293 + isa = PBXGroup;
  1294 + children = (
  1295 + D23696901E7FC83800E4BF71 /* chatMsgModel.h */,
  1296 + D23696911E7FC83800E4BF71 /* chatMsgModel.m */,
  1297 + );
  1298 + path = ChatMsgModel;
  1299 + sourceTree = "<group>";
  1300 + };
1265 1301 D2575C431E76846900E3A8FC /* InteractionSections */ = {
1266 1302 isa = PBXGroup;
1267 1303 children = (
... ... @@ -1610,6 +1646,7 @@
1610 1646 D2029CCD1E6EADE10027CD92 /* SearchModel.m in Sources */,
1611 1647 810B65051E779B1F00133E15 /* NSLayoutConstraint+MASDebugAdditions.m in Sources */,
1612 1648 D2029CCB1E6EADE10027CD92 /* SVRadialGradientLayer.m in Sources */,
  1649 + D23696841E7FC6A900E4BF71 /* chatListCell.m in Sources */,
1613 1650 D2029CAF1E6EADE10027CD92 /* MJRefreshStateHeader.m in Sources */,
1614 1651 D2029C941E6EADE10027CD92 /* UIRefreshControl+AFNetworking.m in Sources */,
1615 1652 D2029C911E6EADE10027CD92 /* UIButton+AFNetworking.m in Sources */,
... ... @@ -1660,6 +1697,7 @@
1660 1697 D2029C981E6EADE10027CD92 /* MJExtensionConst.m in Sources */,
1661 1698 D2575C671E76A25400E3A8FC /* LVCInteractionController.m in Sources */,
1662 1699 D2029CAE1E6EADE10027CD92 /* MJRefreshNormalHeader.m in Sources */,
  1700 + D23696921E7FC83800E4BF71 /* chatMsgModel.m in Sources */,
1663 1701 D2029C901E6EADE10027CD92 /* UIActivityIndicatorView+AFNetworking.m in Sources */,
1664 1702 D2029CC61E6EADE10027CD92 /* SSKeychain.m in Sources */,
1665 1703 D2029C8B1E6EADE10027CD92 /* AFURLResponseSerialization.m in Sources */,
... ... @@ -1671,6 +1709,7 @@
1671 1709 D2029D041E6EAE2E0027CD92 /* UILabel+ContentSize.m in Sources */,
1672 1710 D25530031E79115100DF38A5 /* AnimQueueManager.m in Sources */,
1673 1711 D2029C871E6EADE10027CD92 /* AFHTTPSessionManager.m in Sources */,
  1712 + D236968D1E7FC7C200E4BF71 /* CNBarrageManager.m in Sources */,
1674 1713 D2029B861E6EAAA50027CD92 /* main.m in Sources */,
1675 1714 D2029C8C1E6EADE10027CD92 /* AFURLSessionManager.m in Sources */,
1676 1715 D2029C9F1E6EADE10027CD92 /* NSObject+MJKeyValue.m in Sources */,
... ... @@ -1701,6 +1740,7 @@
1701 1740 D2029BCD1E6EADD70027CD92 /* BaseViewController.m in Sources */,
1702 1741 810B64E01E779AE900133E15 /* LVCPlayer.m in Sources */,
1703 1742 810B64FF1E779B1F00133E15 /* MASConstraint.m in Sources */,
  1743 + D236968E1E7FC7C200E4BF71 /* CNBarrageView.m in Sources */,
1704 1744 D25BF87A1E7B8A82007FB138 /* FilterFunctionView.m in Sources */,
1705 1745 D2029D001E6EAE2E0027CD92 /* NSNumber+ScaleHeight.m in Sources */,
1706 1746 D2888D9E1E6EBF1B003A77C8 /* LVCLiveViewController.m in Sources */,
... ...
LiveVideoCloud/LiveVideoCloud/Classes/Sections/InteractionSections/BottomToolView/BottomToolView.m
... ... @@ -31,12 +31,16 @@
31 31 #pragma mark - buttonClick
32 32 - (void)chatAction
33 33 {
34   -
  34 + if ([self.delegate respondsToSelector:@selector(bottomActionWithType:)]) {
  35 + [self.delegate bottomActionWithType:BtnClickTypeChat];
  36 + }
35 37 }
36 38  
37 39 - (void)privateChatAction
38 40 {
39   -
  41 + if ([self.delegate respondsToSelector:@selector(bottomActionWithType:)]) {
  42 + [self.delegate bottomActionWithType:BtnClickTypePriChat];
  43 + }
40 44 }
41 45  
42 46 - (void)giftAction
... ...
LiveVideoCloud/LiveVideoCloud/Classes/Sections/InteractionSections/Chat/Barrage/CNBarrageManager.h 0 → 100644
  1 +//
  2 +// CNBarrageManager.h
  3 +// personalXmppChatDemo
  4 +//
  5 +// Created by 张旭 on 2016/12/10.
  6 +// Copyright © 2016年 cnlive. All rights reserved.
  7 +//
  8 +
  9 +#import <Foundation/Foundation.h>
  10 +
  11 +@class CNBarrageView;
  12 +@class chatMsgModel;
  13 +@interface CNBarrageManager : NSObject
  14 +
  15 +@property (nonatomic, copy) void(^generateBarrageBlock)(CNBarrageView *view);//生成弹幕View回调
  16 +@property (nonatomic) BOOL isStopBarrage;//是否关闭了弹幕
  17 +/**
  18 + 开始弹幕根据新消息数组
  19 +
  20 + @param newBarrageArray 新消息数组
  21 + */
  22 +- (void)startBarrageWithNewArray:(NSMutableArray <chatMsgModel *>*)newBarrageArray;
  23 +
  24 +/**
  25 + 停止弹幕
  26 + */
  27 +- (void)stopBarrage;
  28 +
  29 +
  30 +
  31 +@end
... ...
LiveVideoCloud/LiveVideoCloud/Classes/Sections/InteractionSections/Chat/Barrage/CNBarrageManager.m 0 → 100644
  1 +//
  2 +// CNBarrageManager.m
  3 +// personalXmppChatDemo
  4 +//
  5 +// Created by 张旭 on 2016/12/10.
  6 +// Copyright © 2016年 cnlive. All rights reserved.
  7 +//
  8 +
  9 +#import "CNBarrageManager.h"
  10 +#import "CNBarrageView.h"
  11 +#import "chatMsgModel.h"
  12 +
  13 +#define MAX_OVERLAP_COUNT 100
  14 +
  15 +@interface CNBarrageManager ()
  16 +
  17 +@property (nonatomic, strong) NSMutableArray *_Nullable allComments;//所有评论
  18 +@property (nonatomic, strong) NSMutableArray *_Nullable tmpComments;//临时评论
  19 +@property (nonatomic ,strong) NSMutableArray *_Nullable barrageQueue;//评论管理队列数组
  20 +@property (nonatomic ,strong) NSMutableArray *_Nullable trajectoryArr;//弹幕轨迹数组
  21 +
  22 +@property BOOL isStarted;//标记是否开始弹幕
  23 +@property BOOL isStopAnimation;//标记是否停止弹幕
  24 +
  25 +@end
  26 +
  27 +@implementation CNBarrageManager
  28 +
  29 +/**
  30 + 开始弹幕根据新消息数组
  31 +
  32 + @param newBarrageArray 新消息数组
  33 + */
  34 +- (void)startBarrageWithNewArray:(NSMutableArray <chatMsgModel *>*)newBarrageArray
  35 +{
  36 + if (self.isStopAnimation && _isStopBarrage) {
  37 + return;
  38 + }
  39 + [self.allComments addObjectsFromArray:newBarrageArray];
  40 +
  41 + [self startBarrage];
  42 +}
  43 +
  44 +/**
  45 + 开始弹幕
  46 + */
  47 +- (void)startBarrage
  48 +{
  49 + if (self.tmpComments.count == 0) {
  50 + [self.tmpComments addObjectsFromArray:self.allComments];
  51 + [self.allComments removeAllObjects];
  52 + }
  53 +
  54 + self.isStarted = YES;
  55 + self.isStopAnimation = NO;
  56 + [self initBarrageCommentView];
  57 +}
  58 +
  59 +/**
  60 + 停止弹幕
  61 + */
  62 +- (void)stopBarrage
  63 +{
  64 + self.isStopAnimation = YES;
  65 +
  66 + [self.barrageQueue enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
  67 + CNBarrageView *view = obj;
  68 + [view stopAnimation];
  69 + view = nil;
  70 + self.tmpComments = nil;
  71 + self.allComments = nil;
  72 + self.trajectoryArr = nil;
  73 + }];
  74 + self.barrageQueue = nil;
  75 +}
  76 +
  77 +/**
  78 + 初始化弹幕的评论View
  79 + */
  80 +- (void)initBarrageCommentView
  81 +{
  82 + if(self.isStopAnimation)return;
  83 + //初始化轨道位置随机
  84 + long count = self.tmpComments.count;
  85 + for (int i = 0; i < count; i ++) {
  86 + chatMsgModel *model = [self.tmpComments firstObject];
  87 + NSString *comment = model.content;
  88 + NSString *userName = model.name;
  89 + if (comment) {
  90 + [self.tmpComments removeObjectAtIndex:0];
  91 + //根据当前未发送弹幕数量生成轨迹
  92 + NSString *trajectory;
  93 + if (self.tmpComments.count > MAX_OVERLAP_COUNT)
  94 + {//数量较多轨迹可重叠
  95 + trajectory = [self trajectoryOverlap:YES];
  96 + }else
  97 + {//数量较少轨迹可能不重叠,但频率高会使轨迹数组已满,造成重叠
  98 + trajectory = [self trajectoryOverlap:NO];
  99 + }
  100 + [self createBarrageComment:comment userName:userName trajectory:trajectory isMine:model.isMine]; //trajectory:trajectory isMine:model.isMine];
  101 + }
  102 + else {
  103 + //当弹幕没有时跳出循环
  104 + break;
  105 + }
  106 + }
  107 +}
  108 +
  109 +/**
  110 + 生成评论弹幕轨迹
  111 +
  112 + @param isOverlap 是否重叠
  113 + @return 根据轨迹是否可重叠,生成对应的新轨迹
  114 + */
  115 +- (NSString *)trajectoryOverlap:(BOOL)isOverlap
  116 +{
  117 + NSString *trajectory = @"";
  118 + long arcIndex = arc4random() % MAX_BARRAGE_VERTICAL_COUNT;
  119 + CGFloat tra = arcIndex * BARRAGE_VERTICAL_HEIGHT + (arcIndex - 1) * BARRAGE_VERTICAL_MARGIN;
  120 +// NSLog(@"trajectory = %f ,%ld,%f,%ld,%f",tra,arcIndex,BARRAGE_VERTICAL_MARGIN,MAX_BARRAGE_VERTICAL_COUNT,BARRAGE_VERTICAL_MARGIN);
  121 + if (isOverlap) {
  122 + //生成可重叠的轨迹
  123 + trajectory = [NSString stringWithFormat:@"%f",tra];
  124 + [self.trajectoryArr addObject:trajectory];
  125 + return [NSString stringWithFormat:@"%f",tra];
  126 + }
  127 + //生成不重叠的轨迹
  128 + do {
  129 + arcIndex = arc4random() % MAX_BARRAGE_VERTICAL_COUNT;
  130 + tra = arcIndex * BARRAGE_VERTICAL_HEIGHT + (arcIndex - 1) * BARRAGE_VERTICAL_MARGIN;
  131 + trajectory = [NSString stringWithFormat:@"%f",tra];
  132 + if (self.trajectoryArr.count >= MAX_BARRAGE_VERTICAL_COUNT) break;
  133 +
  134 + } while ([self.trajectoryArr containsObject:trajectory]);
  135 + if (self.trajectoryArr.count >= MAX_BARRAGE_VERTICAL_COUNT)
  136 + {//当前轨迹数组已满,弹幕需要重叠显示
  137 +
  138 + }else
  139 + {//当前轨迹数组还没满,弹幕无需重叠
  140 + [self.trajectoryArr addObject:trajectory];
  141 + }
  142 + return trajectory;
  143 +}
  144 +
  145 +/**
  146 + 下一条评论
  147 +
  148 + @return 返回当前tmpComments的下一条评论数据
  149 + */
  150 +- (NSString *)nextComment {
  151 + chatMsgModel *model = [self.tmpComments firstObject];
  152 + if (model.content) {
  153 + [self.tmpComments removeObjectAtIndex:0];
  154 + }
  155 + return model.content;
  156 +}
  157 +
  158 +/**
  159 + 创建评论弹幕,根据评论内容和弹幕轨迹
  160 +
  161 + @param comment 评论内容
  162 + @param userName 用户名字
  163 + @param trajectory 弹幕轨迹
  164 + @param isMine 是否是我发的弹幕
  165 + */
  166 +- (void)createBarrageComment:(NSString *)comment userName:(NSString *)userName trajectory:(NSString *)trajectory isMine:(BOOL)isMine
  167 +{
  168 + if (self.isStopAnimation) {
  169 + return;
  170 + }
  171 + //创建一个弹幕view
  172 + CNBarrageView *view = [[CNBarrageView alloc] initWithContent:comment userName:userName isMine:isMine];
  173 +
  174 + //设置运行轨迹
  175 + view.trajectory = [trajectory floatValue];
  176 + weakself
  177 + __weak CNBarrageView *weakBarrageView = view;
  178 + view.moveBlock = ^(CommentMoveState status){
  179 + strongself
  180 + if (self.isStopAnimation) {
  181 + return;
  182 + }
  183 + switch (status) {
  184 + case CommentMoveStateStart:
  185 + {
  186 + //弹幕开始,将view加入弹幕管理数组
  187 + [self.barrageQueue addObject:weakBarrageView];
  188 + }
  189 + break;
  190 + case CommentMoveStateEnter:
  191 + {
  192 + [self.trajectoryArr removeObject:trajectory];
  193 + //弹幕完全进入屏幕,判断接下来是否还有内容,如果有则在该弹幕轨迹队列中创建弹幕
  194 + NSString *comment = [self nextComment];
  195 + if (comment) {
  196 + [self createBarrageComment:comment userName:userName trajectory:trajectory isMine:isMine];
  197 +// [self createBarrageComment:comment trajectory:trajectory isMine:isMine];
  198 + }
  199 + else {
  200 + //没有评论了
  201 + }
  202 + }
  203 + break;
  204 + case CommentMoveStateEnd:
  205 + {
  206 + //弹幕飞出屏幕后从弹幕管理队列中删除
  207 + if ([self.barrageQueue containsObject:weakBarrageView]) {
  208 + [self.barrageQueue removeObject:weakBarrageView];
  209 + }
  210 + }
  211 + break;
  212 + default:
  213 + break;
  214 + }
  215 + };
  216 + //弹幕生成后,传到Controller进行页面展示
  217 + if (self.generateBarrageBlock) {
  218 + self.generateBarrageBlock(view);
  219 + }
  220 +}
  221 +
  222 +#pragma mark - getter
  223 +
  224 +- (NSMutableArray *)allComments {
  225 + if (!_allComments) {
  226 + _allComments = [NSMutableArray array];
  227 + }
  228 + return _allComments;
  229 +}
  230 +
  231 +- (NSMutableArray *)tmpComments {
  232 + if (!_tmpComments) {
  233 + _tmpComments = [NSMutableArray array];
  234 + }
  235 + return _tmpComments;
  236 +}
  237 +
  238 +- (NSMutableArray *)barrageQueue {
  239 + if (!_barrageQueue) {
  240 + _barrageQueue = [NSMutableArray array];
  241 + }
  242 + return _barrageQueue;
  243 +}
  244 +
  245 +- (NSMutableArray *)trajectoryArr {
  246 + if (!_trajectoryArr) {
  247 + _trajectoryArr = [NSMutableArray array];
  248 + }
  249 + return _trajectoryArr;
  250 +}
  251 +
  252 +@end
... ...
LiveVideoCloud/LiveVideoCloud/Classes/Sections/InteractionSections/Chat/Barrage/CNBarrageView.h 0 → 100644
  1 +//
  2 +// CNBarrageView.h
  3 +// personalXmppChatDemo
  4 +//
  5 +// Created by 张旭 on 2016/12/10.
  6 +// Copyright © 2016年 cnlive. All rights reserved.
  7 +//
  8 +
  9 +#import <UIKit/UIKit.h>
  10 +
  11 +#define BARRAGE_VERTICAL_HEIGHT 25.f //每个弹幕View在垂直方向上的高度
  12 +#define MAX_BARRAGE_VERTICAL_COUNT (long)([UIScreen mainScreen].bounds.size.height * 1.0/BARRAGE_VERTICAL_HEIGHT) //垂直方向上的弹幕最大数量
  13 +#define BARRAGE_VERTICAL_MARGIN ([UIScreen mainScreen].bounds.size.height - BARRAGE_VERTICAL_HEIGHT * MAX_BARRAGE_VERTICAL_COUNT) * 1.0 / (MAX_BARRAGE_VERTICAL_COUNT - 1) //垂直方向上弹幕之间的间隙
  14 +
  15 +typedef NS_ENUM(NSInteger, CommentMoveState){//弹幕移动的状态
  16 + CommentMoveStateStart = 0,//将要进入屏幕
  17 + CommentMoveStateEnter ,//完全进入
  18 + CommentMoveStateEnd //完全移除屏幕
  19 +};
  20 +
  21 +
  22 +@interface CNBarrageView : UIView
  23 +
  24 +@property (nonatomic ,copy) void (^moveBlock) (CommentMoveState state);//弹幕移动的状态
  25 +@property (nonatomic) CGFloat trajectory;//弹幕的移动轨迹
  26 +
  27 +/**
  28 + 初始化弹幕View根据评论内容,消息是否是自己发送
  29 +
  30 + @param content 评论内容
  31 + @param userName 用户名字
  32 + @param isMine 是否是自己的评论弹幕
  33 + @return 弹幕View
  34 + */
  35 +- (instancetype)initWithContent:(NSString *)content userName:(NSString *)userName isMine:(BOOL)isMine;
  36 +
  37 +/**
  38 + 开始移动动画
  39 + */
  40 +- (void)startAnimation;
  41 +
  42 +/**
  43 + 停止移动动画
  44 + */
  45 +- (void)stopAnimation;
  46 +
  47 +@end
... ...
LiveVideoCloud/LiveVideoCloud/Classes/Sections/InteractionSections/Chat/Barrage/CNBarrageView.m 0 → 100644
  1 +
  2 +//
  3 +// CNBarrageView.m
  4 +// personalXmppChatDemo
  5 +//
  6 +// Created by 张旭 on 2016/12/10.
  7 +// Copyright © 2016年 cnlive. All rights reserved.
  8 +//
  9 +
  10 +#import "CNBarrageView.h"
  11 +
  12 +#define TotalDuration 5 //弹幕飘过总时间
  13 +#define BARRAGE_Horizontal_Margin 5 //弹幕间水平间隙
  14 +
  15 +@interface CNBarrageView ()
  16 +
  17 +@property (nonatomic ,strong) UILabel * _Nullable commentLabel;//评论Label
  18 +@property BOOL isDealloc;//标记资源是否已释放
  19 +
  20 +@end
  21 +
  22 +@implementation CNBarrageView
  23 +
  24 +/**
  25 + 销毁对象及动画
  26 + */
  27 +- (void)dealloc
  28 +{
  29 + [self stopAnimation];
  30 + self.moveBlock = nil;
  31 +}
  32 +
  33 +/**
  34 + 初始化弹幕View根据评论内容,消息是否是自己发送
  35 +
  36 + @param content 评论内容
  37 + @param userName 用户名字
  38 + @param isMine 是否是自己的评论弹幕
  39 + @return 弹幕View
  40 + */
  41 +- (instancetype)initWithContent:(NSString *)content userName:(NSString *)userName isMine:(BOOL)isMine
  42 +{
  43 + if (self = [super init])
  44 + {
  45 + content = [NSString stringWithFormat:@"%@:%@",userName,content];
  46 + NSDictionary *attributes = @{NSFontAttributeName:[UIFont systemFontOfSize:14]};
  47 + float width = [content sizeWithAttributes:attributes].width;
  48 +// float height = [content sizeWithAttributes:attributes].height;//16.707031
  49 + self.bounds = CGRectMake(0, 0, width + BARRAGE_Horizontal_Margin * 2, BARRAGE_VERTICAL_HEIGHT);
  50 + self.commentLabel.text = content;
  51 + self.commentLabel.frame = CGRectMake(BARRAGE_Horizontal_Margin, 0, width, BARRAGE_VERTICAL_HEIGHT);
  52 + if(isMine)
  53 + {
  54 + self.commentLabel.textColor = [UIColor redColor];
  55 + }else
  56 + {
  57 + self.commentLabel.textColor = [UIColor lightGrayColor];
  58 + }
  59 +
  60 + }
  61 + return self;
  62 +}
  63 +
  64 +/**
  65 + 开始移动动画
  66 + */
  67 +- (void)startAnimation
  68 +{
  69 + CGFloat wholeWidth = self.width + SCREEN_WIDTH + 50;//总轨迹长度
  70 + CGFloat speed = wholeWidth / TotalDuration;//速度
  71 + CGFloat duration = (self.width + 50) / speed;//完全进入屏幕的时间
  72 +
  73 + if (self.moveBlock) {
  74 + //弹幕开始进入屏幕
  75 + self.moveBlock(CommentMoveStateStart);
  76 + }
  77 +
  78 + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(duration * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  79 + //避免重复,若资源已释放 不再操作
  80 + if (self.isDealloc) {
  81 + return;
  82 + }
  83 + //duration时间后弹幕完全进入屏幕
  84 + if (self.moveBlock) {
  85 + self.moveBlock(CommentMoveStateEnter);
  86 + }
  87 + });
  88 +
  89 + weakself
  90 + //弹幕完全离开屏幕
  91 + [UIView animateWithDuration:TotalDuration delay:0 options:UIViewAnimationOptionCurveLinear animations:^{
  92 + strongself
  93 + self.left = - self.width;
  94 + } completion:^(BOOL finished) {
  95 + if (self.moveBlock) {
  96 + self.moveBlock(CommentMoveStateEnd);
  97 + }
  98 + [self removeFromSuperview];
  99 + }];
  100 +
  101 +}
  102 +
  103 +/**
  104 + 停止移动动画
  105 + */
  106 +- (void)stopAnimation
  107 +{
  108 + self.isDealloc = YES;
  109 + [self.layer removeAllAnimations];
  110 + [self removeFromSuperview];
  111 +}
  112 +
  113 +#pragma mark getter
  114 +
  115 +- (UILabel *)commentLabel
  116 +{
  117 + if (!_commentLabel) {
  118 + _commentLabel = [[UILabel alloc] init];
  119 + _commentLabel.backgroundColor = [UIColor clearColor];
  120 + _commentLabel.font = [UIFont systemFontOfSize:14.f];
  121 + _commentLabel.textColor = [UIColor lightGrayColor];
  122 + [self addSubview:self.commentLabel];
  123 + }
  124 + return _commentLabel;
  125 +}
  126 +
  127 +/*
  128 +// Only override drawRect: if you perform custom drawing.
  129 +// An empty implementation adversely affects performance during animation.
  130 +- (void)drawRect:(CGRect)rect {
  131 + // Drawing code
  132 +}
  133 +*/
  134 +
  135 +@end
... ...
LiveVideoCloud/LiveVideoCloud/Classes/Sections/InteractionSections/Chat/ChatMsgModel/chatMsgModel.h 0 → 100644
  1 +//
  2 +// chatMsgModel.h
  3 +// LiveVideoCloud
  4 +//
  5 +// Created by 张旭 on 2017/3/20.
  6 +// Copyright © 2017年 cnlive. All rights reserved.
  7 +//
  8 +
  9 +#import "BaseModel.h"
  10 +
  11 +@interface chatMsgModel : BaseModel
  12 +
  13 +@property (nonatomic ,copy ) NSString * _Nullable icon;//头像
  14 +@property (nonatomic ,copy ) NSString * _Nullable name;//姓名
  15 +@property (nonatomic ,copy ) NSString * _Nullable content;//内容
  16 +
  17 +@property (nonatomic ,assign) CGFloat cellWidth;//内容宽度
  18 +@property (nonatomic ,assign) CGFloat cellHeight;//内容高度
  19 +
  20 +@property (nonatomic) BOOL isMine;//消息是否是自己发送
  21 +
  22 +@end
... ...
LiveVideoCloud/LiveVideoCloud/Classes/Sections/InteractionSections/Chat/ChatMsgModel/chatMsgModel.m 0 → 100644
  1 +//
  2 +// chatMsgModel.m
  3 +// LiveVideoCloud
  4 +//
  5 +// Created by 张旭 on 2017/3/20.
  6 +// Copyright © 2017年 cnlive. All rights reserved.
  7 +//
  8 +
  9 +#import "chatMsgModel.h"
  10 +
  11 +#define ChatCellMargin 10
  12 +
  13 +@implementation chatMsgModel
  14 +
  15 +- (CGFloat)cellHeight
  16 +{
  17 + //如果计算过高度,直接返回
  18 + if (_cellHeight) return _cellHeight;
  19 +
  20 + //头像
  21 + _cellHeight = 40;
  22 + //文字
  23 + CGFloat textMaxW = self.cellWidth - 2 * ChatCellMargin;
  24 + CGSize textMaxSize = CGSizeMake(textMaxW, MAXFLOAT);
  25 + CGSize textSize = [self.content boundingRectWithSize:textMaxSize options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName : [UIFont systemFontOfSize:16]} context:nil].size;
  26 +
  27 + //底部空隙
  28 + _cellHeight += textSize.height + ChatCellMargin;
  29 + // NSLog(@"cellHeight = %f",_cellHeight);
  30 + return _cellHeight;
  31 +}
  32 +@end
... ...
LiveVideoCloud/LiveVideoCloud/Classes/Sections/InteractionSections/Chat/ChatViews/chatListCell.h 0 → 100644
  1 +//
  2 +// chatListCell.h
  3 +// LiveVideoCloud
  4 +//
  5 +// Created by 张旭 on 2017/3/20.
  6 +// Copyright © 2017年 cnlive. All rights reserved.
  7 +//
  8 +
  9 +#import "BaseTableViewCell.h"
  10 +@class chatMsgModel;
  11 +@interface chatListCell : BaseTableViewCell
  12 +
  13 +- (void)loadUIWithModel:(chatMsgModel *)model;
  14 +
  15 +@end
... ...
LiveVideoCloud/LiveVideoCloud/Classes/Sections/InteractionSections/Chat/ChatViews/chatListCell.m 0 → 100644
  1 +//
  2 +// chatListCell.m
  3 +// LiveVideoCloud
  4 +//
  5 +// Created by 张旭 on 2017/3/20.
  6 +// Copyright © 2017年 cnlive. All rights reserved.
  7 +//
  8 +
  9 +#import "chatListCell.h"
  10 +#import "chatMsgModel.h"
  11 +@interface chatListCell ()
  12 +
  13 +@property (nonatomic ,strong) UIImageView * _Nullable iconImageView;//头像图片
  14 +@property (nonatomic ,strong) UILabel * _Nullable nameLabel;//名字
  15 +@property (nonatomic ,strong) UILabel * _Nullable contentLabel;//内容
  16 +@property (nonatomic ,strong) UIImageView * _Nullable lineImageView;//分割线
  17 +
  18 +@property (nonatomic ,strong) chatMsgModel * _Nullable model;
  19 +
  20 +@end
  21 +
  22 +@implementation chatListCell
  23 +
  24 +- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
  25 +{
  26 + self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
  27 + if (self) {
  28 + self.backgroundColor = [UIColor clearColor];
  29 + self.selectionStyle = UITableViewCellSelectionStyleNone;
  30 + }
  31 + return self;
  32 +}
  33 +
  34 +- (void)loadUIWithModel:(chatMsgModel *)model
  35 +{
  36 + model.cellWidth = self.width;
  37 + _model = model;
  38 +
  39 + [self bindView];//更新cell高度后再初始化UI
  40 + [self.iconImageView sd_setImageWithURL:[NSURL URLWithString:model.icon] placeholderImage:nil];
  41 + // self.iconImageView.backgroundColor = [UIColor greenColor];
  42 + self.nameLabel.text = [NSString stringWithFormat:@"%@:",model.name];
  43 + CGSize labelSize = [self.nameLabel labelSize];
  44 + self.nameLabel.frame = CGRectMake(self.iconImageView.right + 10, self.iconImageView.top, labelSize.width, labelSize.height);
  45 + self.nameLabel.centerY = self.iconImageView.centerY;
  46 + self.contentLabel.text = model.content;
  47 + CGSize contentLabelSize = [self.contentLabel labelSizeWithWidth:self.width - 2 * self.iconImageView.left];
  48 + self.contentLabel.frame = CGRectMake(self.iconImageView.left, self.iconImageView.bottom + 10, contentLabelSize.width, contentLabelSize.height);
  49 +
  50 + self.lineImageView.frame = CGRectMake(self.iconImageView.left, self.contentLabel.bottom + 5, self.width - self.iconImageView.left * 2, 1);
  51 +}
  52 +
  53 +- (void)bindView
  54 +{
  55 + self.iconImageView.frame = CGRectMake(10, 10, 30, 30);
  56 + self.nameLabel.frame = CGRectMake(self.iconImageView.right + 10, self.iconImageView.top, self.width - self.iconImageView.right - 20, 25);
  57 + self.nameLabel.centerY = self.iconImageView.centerY;
  58 + self.contentLabel.frame = CGRectMake(self.iconImageView.left, self.iconImageView.bottom + 10, self.width - 2 * self.iconImageView.left, 25);
  59 +}
  60 +
  61 +#pragma mark - getter
  62 +- (UILabel * _Nullable)nameLabel
  63 +{
  64 + if (!_nameLabel)
  65 + {
  66 + _nameLabel = [[UILabel alloc] init];
  67 + _nameLabel.textAlignment = NSTextAlignmentLeft;
  68 + _nameLabel.textColor = [UIColor redColor];
  69 + _nameLabel.font = [UIFont systemFontOfSize:15.f];
  70 + [self addSubview:_nameLabel];
  71 + }
  72 + return _nameLabel;
  73 +}
  74 +
  75 +- (UILabel * _Nullable)contentLabel
  76 +{
  77 + if (!_contentLabel)
  78 + {
  79 + _contentLabel = [[UILabel alloc] init];
  80 + _contentLabel.textAlignment = NSTextAlignmentLeft;
  81 + _contentLabel.textColor = [UIColor lightGrayColor];
  82 + _contentLabel.numberOfLines = 0;
  83 + _contentLabel.font = [UIFont systemFontOfSize:12.f];
  84 + [self addSubview:_contentLabel];
  85 + }
  86 + return _contentLabel;
  87 +}
  88 +
  89 +- (UIImageView * _Nullable)iconImageView
  90 +{
  91 + if (!_iconImageView)
  92 + {
  93 + _iconImageView = [[UIImageView alloc] init];
  94 + _iconImageView.layer.masksToBounds = YES;
  95 + _iconImageView.layer.cornerRadius = 15.f;
  96 + [self addSubview:_iconImageView];
  97 + }
  98 + return _iconImageView;
  99 +}
  100 +
  101 +- (UIImageView * _Nullable)lineImageView
  102 +{
  103 + if (!_lineImageView)
  104 + {
  105 + _lineImageView = [[UIImageView alloc] init];
  106 + _lineImageView.image = [UIImage imageNamed:@"分割线(聊天复用)"];
  107 + [self addSubview:_lineImageView];
  108 + }
  109 + return _lineImageView;
  110 +}
  111 +
  112 +
  113 +/*
  114 +// Only override drawRect: if you perform custom drawing.
  115 +// An empty implementation adversely affects performance during animation.
  116 +- (void)drawRect:(CGRect)rect {
  117 + // Drawing code
  118 +}
  119 +*/
  120 +
  121 +@end
... ...
LiveVideoCloud/LiveVideoCloud/Classes/Sections/InteractionSections/LVCAnimationManager.m
... ... @@ -65,7 +65,7 @@ static LVCAnimationManager * __singleton__;
65 65 */
66 66 - (void)bubblingWithCenterPoint:(CGPoint)centerPoint toView:(UIView *_Nonnull)toView speed:(CGFloat)speed
67 67 {
68   - CGFloat defSpeed = 2;
  68 + CGFloat defSpeed = 1;
69 69 if (speed != 0) {
70 70 defSpeed = speed;
71 71 }
... ...
LiveVideoCloud/LiveVideoCloud/Classes/Sections/InteractionSections/LiveBaseView.m
... ... @@ -13,6 +13,11 @@
13 13 #import "GiftModel.h"
14 14 #import "PresentView.h"
15 15 #import "ComboCell.h"
  16 +#import "LVCAnimationManager.h"
  17 +#import "chatListCell.h"
  18 +#import "chatMsgModel.h"
  19 +
  20 +#define BottomToolHeight 60
16 21  
17 22 typedef NS_ENUM(NSInteger, PopType) {//弹窗类型
18 23 PopTypeGift = 1,//礼物弹窗
... ... @@ -20,7 +25,7 @@ typedef NS_ENUM(NSInteger, PopType) {//弹窗类型
20 25 PopTypePriMessage //私信弹窗
21 26 };
22 27  
23   -@interface LiveBaseView ()<bottomBtnDeledate,PresentViewDelegate,UIScrollViewDelegate>
  28 +@interface LiveBaseView ()<bottomBtnDeledate,PresentViewDelegate,UIScrollViewDelegate,UITableViewDelegate,UITableViewDataSource>
24 29  
25 30 @property (nonatomic ,strong ) UIScrollView * _Nullable scrollView;
26 31 @property (nonatomic ,strong ,readwrite) UIView * _Nullable animationBaseView;//展示层View
... ... @@ -28,8 +33,10 @@ typedef NS_ENUM(NSInteger, PopType) {//弹窗类型
28 33 @property (nonatomic ,strong ) UIButton * _Nullable closeBtn;
29 34 @property (nonatomic ,strong ) UIView * _Nullable giftPopView;
30 35 @property (nonatomic ,strong ,readwrite) PresentView * _Nullable presentView;//连击View
31   -
32 36 @property (nonatomic ,assign ) PopType popType;
  37 +@property (nonatomic ,strong) UITableView * _Nullable chatTableView;
  38 +@property (nonatomic ,strong) NSMutableArray * _Nullable chatMsgArray;//聊天消息数组
  39 +
33 40 @end
34 41  
35 42 @implementation LiveBaseView
... ... @@ -44,6 +51,15 @@ typedef NS_ENUM(NSInteger, PopType) {//弹窗类型
44 51 [self.animationBaseView addSubview:self.presentView];
45 52 [self addSubview:self.closeBtn];
46 53 [self addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapGestureClick:)]];
  54 + [[LVCAnimationManager shareManager] bubblingWithCenterPoint:CGPointMake(SCREEN_WIDTH - 50, SCREEN_HEIGHT - 50) toView:self.animationBaseView speed:1];
  55 +// ChatView *chatView = [[ChatView alloc] initWithFrame:CGRectMake(0, SCREEN_HEIGHT - 150, 200, 100)];
  56 +// chatView.backgroundColor = ZXRandomColor;
  57 +// [self addSubview:chatView];
  58 + [self.animationBaseView addSubview:self.chatTableView];
  59 + self.chatTableView.backgroundColor = ZXRandomColor;
  60 + chatMsgModel *model = [[chatMsgModel alloc] init];
  61 + model.name = @"张旭";
  62 + [self.chatMsgArray addObject:model];
47 63 }
48 64 return self;
49 65 }
... ... @@ -51,7 +67,6 @@ typedef NS_ENUM(NSInteger, PopType) {//弹窗类型
51 67 #pragma mark - bottomBtnDelegate
52 68 - (void)bottomActionWithType:(BtnClickType)btnType
53 69 {
54   - [self hiddenView:YES];
55 70 switch (btnType) {
56 71 case BtnClickTypeChat:
57 72 {
... ... @@ -67,7 +82,7 @@ typedef NS_ENUM(NSInteger, PopType) {//弹窗类型
67 82 case BtnClickTypeGift:
68 83 {
69 84 self.popType = PopTypeGift;
70   -
  85 + [self hiddenView:YES];
71 86 GiftListView *giftListView = [[GiftListView alloc] initWithFrame:CGRectMake(0, SCREEN_HEIGHT - 250, SCREEN_WIDTH, 250) liveBaseView:self];
72 87 [self addSubview:giftListView];
73 88 self.giftPopView = giftListView;
... ... @@ -78,6 +93,8 @@ typedef NS_ENUM(NSInteger, PopType) {//弹窗类型
78 93 giftBg.centerY = giftListView.height * 0.5;
79 94 giftBg.alpha = 0.4;
80 95 [giftListView addSubview:giftBg];
  96 +
  97 +
81 98 }
82 99 break;
83 100 default:
... ... @@ -89,6 +106,7 @@ typedef NS_ENUM(NSInteger, PopType) {//弹窗类型
89 106 {
90 107 self.bottomToolView.hidden = isHidden;
91 108 self.closeBtn.hidden = isHidden;
  109 + self.chatTableView.hidden = isHidden;
92 110 }
93 111  
94 112 - (void)clearCurrentPopView
... ... @@ -120,6 +138,27 @@ typedef NS_ENUM(NSInteger, PopType) {//弹窗类型
120 138 }
121 139  
122 140 }
  141 +#pragma mark - 聊天消息列表
  142 +#pragma mark - UITableViewDataSource
  143 +
  144 +- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
  145 +{
  146 + return self.chatMsgArray.count;
  147 +}
  148 +
  149 +-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
  150 +{
  151 + chatListCell * cell = [tableView dequeueReusableCellWithIdentifier:NSStringFromClass([chatListCell class]) forIndexPath:indexPath];
  152 + [cell loadUIWithModel:self.chatMsgArray[indexPath.row]];
  153 + return cell;
  154 +}
  155 +
  156 +#pragma mark - UITableViewDelegate
  157 +- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
  158 +{
  159 + return [self.chatMsgArray[indexPath.row] cellHeight];
  160 +}
  161 +
123 162 #pragma mark - PresentViewDelegate
124 163  
125 164 - (PresentViewCell *)presentView:(PresentView *)presentView cellOfRow:(NSInteger)row
... ... @@ -141,13 +180,13 @@ typedef NS_ENUM(NSInteger, PopType) {//弹窗类型
141 180  
142 181 - (void)presentView:(PresentView *)presentView animationCompleted:(NSInteger)shakeNumber model:(id<PresentModelAble>)model
143 182 {
144   -// self.number = 5;
145 183 NSLog(@"%@礼物的连送动画执行完成", model.giftName);
146 184 }
147 185  
148 186 #pragma mark - buttonClick
149 187 - (void)tapGestureClick:(UITapGestureRecognizer *)gesture
150 188 {
  189 + [[LVCAnimationManager shareManager] bubblingWithCenterPoint:CGPointMake(SCREEN_WIDTH - 50, SCREEN_HEIGHT - 50) toView:self.animationBaseView];
151 190 [self clearCurrentPopView];
152 191 }
153 192  
... ... @@ -212,7 +251,7 @@ typedef NS_ENUM(NSInteger, PopType) {//弹窗类型
212 251 {
213 252 if (!_bottomToolView)
214 253 {
215   - _bottomToolView = [[BottomToolView alloc] initWithFrame:CGRectMake(0, SCREEN_HEIGHT - 60, SCREEN_WIDTH, 60)];
  254 + _bottomToolView = [[BottomToolView alloc] initWithFrame:CGRectMake(0, SCREEN_HEIGHT - BottomToolHeight, SCREEN_WIDTH, BottomToolHeight)];
216 255 _bottomToolView.delegate = self;
217 256 _bottomToolView.backgroundColor = [UIColor lightGrayColor];
218 257 _bottomToolView.alpha = 0.7;
... ... @@ -220,6 +259,31 @@ typedef NS_ENUM(NSInteger, PopType) {//弹窗类型
220 259 return _bottomToolView;
221 260 }
222 261  
  262 +- (UITableView *)chatTableView
  263 +{
  264 + if (!_chatTableView) {
  265 + UITableView *tableView = [[UITableView alloc]initWithFrame:CGRectMake(10, SCREEN_HEIGHT - 150 - BottomToolHeight - 10, 300, 150)];
  266 + tableView.delegate = self;
  267 + tableView.dataSource = self;
  268 + tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
  269 + [tableView registerClass:[chatListCell class] forCellReuseIdentifier:NSStringFromClass([chatListCell class])];
  270 + tableView.showsVerticalScrollIndicator = NO;
  271 + tableView.showsHorizontalScrollIndicator = NO;
  272 + tableView.backgroundColor = [UIColor clearColor];
  273 + tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
  274 + _chatTableView = tableView;
  275 + }
  276 + return _chatTableView;
  277 +}
  278 +
  279 +- (NSMutableArray *)chatMsgArray
  280 +{
  281 + if (!_chatMsgArray) {
  282 + _chatMsgArray = [NSMutableArray array];
  283 + }
  284 + return _chatMsgArray;
  285 +}
  286 +
223 287 - (UIButton *)closeBtn
224 288 {
225 289 if (!_closeBtn)
... ...