最近把公司项目的聊天模块从”XMPP”转到”网易云信”官网、github。在转的过程中,上手很快,基本上没遇到什么难题,很多程度上感谢云信的NIMKit。以前也接触过几个IM SDK服务商的代码,那个时候看他们的代码根本没什么欲望,但这次看云信的代码有种被吸引的感觉,恨不得一下子把它的代码全部看完,封装的很好,扩展性很强(ps:就我目前的水平只能说出这些优点)。对于一般的聊天UI完全可以满足,就算不用网易的IM SDK,但他们的代码真的值得一下(尽管他们的UIKit代码注释比较少)。 Base tipscell的组成结构对于聊天”MessageCell”的介绍,一定要记住下面这张图片,以及相关参数的解释。5 ^5 X( P* o* \# ^# E
nimkit_cell ) K: Q' {: v; E0 \6 ^& W; E
- 蓝色区域:为具体内容,如文字 UILabel ,图片 UIImageView 等 。(对应的NIMMessageModel对象的contentSize属性)。注:NIMMessageModel为消息(NIMMessage) 在NIMKit中的封装。这个封装主要是为了对计算结果和布局配置进行缓存,以避免反复的计算和读取相同的信息,从而提高应用性能。
- 绿色区域:为消息的气泡,具体的内容和气泡之间会有一定的内间距,这里为 contentViewInsets 。(对应的NIMMessageModel对象的contentViewInsets属性)
- 紫色区域:为整个 UITableViewCell ,具体的气泡和整个cell会有一定的内间距,这里为 cellInsets 。(对应的NIMMessageModel对象的bubbleViewInsets属性)
6 ^1 S" @' f- u9 @* J" d
config配置协议在聊天界面有几个config配置代理,先熟悉一下。 - NIMSessionConfig:消息对应的session配置。如:录音、输入框、表情、更多等操作的选择;点击”+”号出来的多媒体按钮;是否禁用输入控件;输入控件的最大长度;输入控件的placeholder;一次最多消息的消息内容;间隔多久显示时间;语音红点是否禁用;是否自动切换成听筒模式;是否自动获取历史消息;消息数据提供器;消息的排版配置等。可以说这个是贯穿整个聊天模块的配置,修改聊天界面一般就得调整这里。
- NIMCellLayoutConfig:消息对应的布局配置。我们可以在这个config里面根据消息类型是否显示头像、姓名、头像与姓名之间的margin等;然后你会在项目里面看到自定义消息类型对应的NTESSessionCustomLayoutConfig,以及default默认的配置NIMCellLayoutDefaultConfig;
- NIMSessionContentConfig:消息内容配置。这个配置主要是为NIMSessionMessageContentView(请看下面对 聊天 NIMMessageCell.h 的介绍)对象为设置的。
- x/ N9 |( C) s& N
聊天 NIMMessageCell.h先来看看头文件定义的属性
/ m) z, u6 x J4 C4 l - @property (nonatomic, strong) NIMAvatarImageView *headImageView;* H' ~- p( o9 T: U9 c& J( T
- @property (nonatomic, strong) UILabel *nameLabel; //姓名(群显示 个人不显示)& J/ t0 X- d) p/ P
- @property (nonatomic, strong) NIMSessionMessageContentView *bubbleView; //内容区域) |1 t% |2 y5 ~. Z t$ t( t# Z
- @property (nonatomic, strong) UIActivityIndicatorView *traningActivityIndicator; //发送loading
; m' h9 ?7 ?* Y9 L9 p - @property (nonatomic, strong) UIButton *retryButton; //重试
! I, A- O. X- V* s* m0 T7 Y2 P" g - @property (nonatomic, strong) NIMBadgeView *audioPlayedIcon; //语音未读红点
复制代码NIMSessionMessageContentView,顾名思义就是MessageCell的内容View(包括下面的bubble气泡View)。而 NIMSessionContentConfig 配置主要是配置 contentSize、contentViewInsets以及这个配置所应的 messageContentView 类名(NIMSessionMessageContentView的子类,每种聊天类型对应一个messageContentView)。注意,这里并没有提到 bubbleViewInsets,因为气泡隔cell的距离不会因不同类型而改变,我们只需在 cellLayoutConfig 里面处理即可,当然想要做到不同的话,也可以在 NIMSessionContentConfig 配置里面增加一个协议方法。注意 NIMSessionMessageContentView 是继承自 UIControl,这样不仅能处理点击事件,还能很好的处理点击高亮的效果。 NIMSessionViewController(聊天回话控制器基类)先来看看最重要的计算高度方法
+ e# {+ {5 x& ]3 G9 P0 N - - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
0 j9 ]1 f+ B \0 G g4 @ - {
! u+ o. z, M) l4 {8 ?! i5 o - CGFloat cellHeight = 0;
5 @6 X2 b4 v! L - id modelInArray = [[_sessionDatasource modelArray] objectAtIndex:indexPath.row];7 ^2 j5 L+ h) b
- if ([modelInArray isKindOfClass:[NIMMessageModel class]]) L u$ q- P% o
- {6 g7 L4 \" Y' S8 E' C
- NIMMessageModel *model = (NIMMessageModel *)modelInArray;9 \: I3 x/ d5 p" W- n
- NSAssert([model respondsToSelector:@selector(contentSize)], @"config must have a cell height value!!!");
; l/ I1 t, v6 w h% x% G& T - [self layoutConfig:model];
2 u7 L+ g s& Y1 r$ v - CGSize size = model.contentSize;' C6 k; B& X# h4 q+ H1 K9 V4 S
- UIEdgeInsets contentViewInsets = model.contentViewInsets;8 K( j3 j/ T. ]% [- m e
- UIEdgeInsets bubbleViewInsets = model.bubbleViewInsets;
. I7 q. x) h1 _ - cellHeight = size.height + contentViewInsets.top + contentViewInsets.bottom + bubbleViewInsets.top + bubbleViewInsets.bottom;$ R/ q* S. K$ K+ ?, Q
- }
. w# ~/ H) o2 W" V( n) @ - else if ([modelInArray isKindOfClass:[NIMTimestampModel class]])
5 z5 o# ~* C. {$ p7 t. F - {* y3 `" @) D$ J, X3 V
- cellHeight = [modelInArray height];
7 e# i ]' F1 J( h: J - }$ u6 _/ ^/ ]5 m# a! f6 I; M+ W
- else3 j/ v, M$ l0 c$ r: q
- {
- E5 Y. h/ u; c8 u- E - NSAssert(0, @"not support model");) N+ w) u; X$ U, Q* ~7 \
- }0 K0 k7 Y5 N( v
- return cellHeight;
$ O- ~3 c9 n; G - }
复制代码某个数据源所对应的高度就是三个颜色区域的高度之和(contentSize.height + contentViewInsets.top + contentViewInsets.bottom + bubbleViewInsets.top + bubbleViewInsets.bottom)。
1 q+ y$ w6 v4 a4 \- J2 o3 N然后我们在来看看layoutConfig:方法:
$ O) `% y f6 ~ - - (void)layoutConfig:(NIMMessageModel *)model{
' l" G# e9 H7 s3 q# I% q - model.sessionConfig = self.sessionConfig;
% ^) @6 w) G$ p; J. p+ n - if (model.layoutConfig == nil)
; ?6 b ~, m0 z; u - {
7 ^% l& }' ^" S1 K4 t! _3 h - id layoutConfig = nil;
+ }2 z3 w ^! ~: e - if ([self.sessionConfig respondsToSelector:@selector(layoutConfigWithMessage:)]) {
1 Z6 h/ c7 t4 P( d - layoutConfig = [self.sessionConfig layoutConfigWithMessage:model.message];9 a+ e% I7 _; C; |
- }
, i8 E$ O x$ G |- E: ^; ]) ? - if (!layoutConfig) {
; }, M* {! u3 e7 _6 p! E - layoutConfig = [NIMDefaultValueMaker sharedMaker].cellLayoutDefaultConfig;* A$ M. G# [9 W, O% l. H! R
- }
7 [) V/ ~. I3 n2 Y6 \' h0 D - model.layoutConfig = layoutConfig;
/ ~& E7 I1 S& j6 ]9 | - }
2 o6 c5 ]/ F7 ], x2 E e+ G - [model calculateContent:self.tableView.nim_width];
" D: @& A0 b9 R8 R1 P. C" G4 R5 R - }
复制代码其实这里就是,先配置model的sessionConfig,然后配置layoutConfig,配置完后就去计算该model所对应内容的contentSize。注:layoutConfigWithMessage:方法是自定义消息类型需要处理,还有记得在写代码中做好判nil的处理,如果为nil的话给default值。 好了,现在到了一个我当时比较蛋疼的地方了,请看下图 U$ |8 u7 a- {( |8 ^+ L8 ]8 N9 s
Image2
, x( ~" A. I6 G2 @: b; k看到很多的方法,仔细看看,除了layoutConfig的配置方法以外,还有很多Attachment(Attachment 是属于自定义消息的配置协议)结尾的方法,其实这里应该只会提示NIMCellLayoutDefaultConfig和NTESSessionCustomLayoutConfig(NTESChatroomCellLayoutConfig聊天室的布局配置请忽略)才对,它们只是方法名相同,应该是Xcode抽风而导致的。 NIMCellLayoutDefaultConfig计算contentSize先看三个相关部分的代码 NIMCellLayoutDefaultConfig- - (CGSize)contentSize:(NIMMessageModel *)model cellWidth:(CGFloat)cellWidth{
# ~' ^) H* K+ g - , ]) P2 _; n: O3 [0 d; B1 N
- idconfig = [[NIMSessionContentConfigFactory sharedFacotry] configBy:model.message];; _" }1 n8 _# p1 Z; ~
- return [config contentSize:cellWidth];
" g" W% j3 G. P$ ? - }
_: z/ q9 w5 Y) I+ x( w - $ o" G( L- a0 N3 ?6 [
- - (NSString *)cellContent:(NIMMessageModel *)model{8 w5 N8 p% F7 u2 q0 S
-
* X& H8 r! l# ^# R6 Q5 k' [ - idconfig = [[NIMSessionContentConfigFactory sharedFacotry] configBy:model.message];
! x- m* [" F; \, @! z7 H9 R - NSString *cellContent = [config cellContent];
4 I5 c3 a1 X# R$ [3 V. n - return cellContent ? : @"NIMSessionUnknowContentView";+ r( p. f: e( ?; H! y
- }
0 _5 @: s S5 ]! L; r$ i
# X6 P* [7 |7 A: H* A
! S3 I, U: f. r& P: m2 @- - (UIEdgeInsets)contentViewInsets:(NIMMessageModel *)model{7 R$ `$ G/ S' U$ m! l5 ]0 ^ v% u4 Y
- idconfig = [[NIMSessionContentConfigFactory sharedFacotry] configBy:model.message];& W6 o8 ~( L8 W' F4 C( e, X$ M/ q
- return [config contentViewInsets];
7 B7 F+ n' J! y+ n - }
复制代码 NIMSessionContentConfigFactory- - (instancetype)init( C# ^$ P- x: s; v
- {
- Z9 {) e% [; |2 R2 @ - if (self = [super init])
$ E: l4 l0 r8 T$ _( \* Q6 I - {3 \0 q Y( i" S F3 o0 f
- _dict = @{@(NIMMessageTypeText) : [NIMTextContentConfig new],
/ M4 x( O: c7 h+ b, Y( ? - @(NIMMessageTypeImage) : [NIMImageContentConfig new],7 T; N4 g9 R& [# x$ h* i3 C/ b
- @(NIMMessageTypeAudio) : [NIMAudioContentConfig new],
" I" H6 @( f. M8 F4 b9 g- U5 D - @(NIMMessageTypeVideo) : [NIMVideoContentConfig new],
7 s: E# J: g) p H @3 n - @(NIMMessageTypeFile) : [NIMFileContentConfig new],
9 N3 ^8 Z4 t2 t* @+ e& e - @(NIMMessageTypeLocation) : [NIMLocationContentConfig new],9 U9 l) S* m7 ]# ?& L9 O/ B, i5 Z, x d
- @(NIMMessageTypeNotification) : [NIMNotificationContentConfig new],
# ?6 d% J; J5 i! B' A - @(NIMMessageTypeTip) : [NIMTipContentConfig new]};4 C3 Y9 _2 m' j# D. y
- }
2 H3 e) H1 |3 M: [. K - return self;5 d5 U' K1 `: A5 B
- }4 n9 \5 M. G* s/ j+ `# y3 ?. ]
# t R4 o3 _+ V* F5 c, _) ^6 b- - (id)configBy:(NIMMessage *)message+ x1 j2 K/ R$ }0 y6 S" n
- {, R+ j2 X# A) A# X1 G! ~
- NIMMessageType type = message.messageType;
( `- k: M" }5 E- r+ W5 ^ - idconfig = [_dict objectForKey:@(type)];6 j4 V/ ~2 E3 E8 H* s2 r
- if (config == nil)" i/ D4 v5 d" [7 k9 ~2 r l1 ]
- {
, W y* o) G$ l% [ z - config = [NIMUnsupportContentConfig sharedConfig];- [/ x% X) |$ V
- }3 b$ l# r, k. `6 n0 a; G$ \
- if ([config isKindOfClass:[NIMBaseSessionContentConfig class]])+ Y+ ~3 P9 z% D: S: I; a
- {
3 O0 j! {4 {: N6 m5 ]4 J9 v7 \ - [(NIMBaseSessionContentConfig *)config setMessage:message];
1 m3 H3 y5 X: v- b L- d; E - }! D( W& s' J5 ^1 ?
- return config;
6 _4 A7 ~. x `' ], V, A& ~ - }
复制代码 NIMImageContentConfig- #import "NIMBaseSessionContentConfig.h"/ d3 C/ z- k; O3 c1 A
1 q; H4 E0 l4 @/ B! ?3 h1 Z- @interface NIMTextContentConfig : NIMBaseSessionContentConfig2 q) Q& a, l% S1 g- O
0 V/ ?) F0 V: O+ X( q0 P* r- @end
复制代码- @interface NIMTextContentConfig()* M! t5 l+ D9 m; a3 s
- 9 u5 n! A; z/ Y2 ~2 |2 n& g
- @property (nonatomic,strong) NIMAttributedLabel *label;
6 |8 e0 D9 p8 i( Z( ]8 t+ ~0 F - 8 V! Z" \' ^4 \: S e$ m9 L9 m3 d
- @end
$ x% o$ S+ s6 x4 h6 K9 W- I- U/ H; P
# o% F2 y/ G) b
/ x6 r- @ N% Z4 |- H5 C M1 G6 k) J- @implementation NIMTextContentConfig8 s8 o7 M: i+ k* b# C/ Y3 ^# \
- 8 t2 ^. f$ |% U3 j" f) L [& t
- - (CGSize)contentSize:(CGFloat)cellWidth! T" _+ ?* A* f7 Q
- {9 F7 u8 I! \2 y4 }' [
- NSString *text = self.message.text;
3 Y. ~3 j3 i5 J0 w* y; y9 H+ \ - [self.label nim_setText:text];
+ ?" G V6 @& N- n+ I9 ~; p0 C: b - + J5 Q' v& l+ D; a! ~
- CGFloat msgBubbleMaxWidth = (cellWidth - 130);' H( ]4 E Z9 R0 @* L5 @8 `
- CGFloat bubbleLeftToContent = 14;/ @5 M% h7 ?4 E) y: T
- CGFloat contentRightToBubble = 14;* h0 u. r. z7 J: n( S* k
- CGFloat msgContentMaxWidth = (msgBubbleMaxWidth - contentRightToBubble - bubbleLeftToContent);
. `- r" G! O/ j0 t0 y% b# E& X - return [self.label sizeThatFits:CGSizeMake(msgContentMaxWidth, CGFLOAT_MAX)];
& z$ T% O2 M$ T5 {) y - }( w+ f. K2 t$ i" R' d: R4 a: E l+ W
- 0 X. }' K3 V1 } Q4 I) U
- - (NSString *)cellContent
: Y, s( q5 z& x& d2 `- a - {
1 E" T8 D; d2 d; b* H2 p ?6 F* J - return @"NIMSessionTextContentView";3 K+ L" V( v! a1 r3 j* U
- }9 n" ]; }0 k3 l. Y( O9 e
- % p _- p9 `' z
- - (UIEdgeInsets)contentViewInsets
5 \! {0 \& N' o% A - {7 T+ [* [" x: {3 N- h) Z
- return self.message.isOutgoingMsg ? UIEdgeInsetsMake(11,11,9,15) : UIEdgeInsetsMake(11,15,9,9);( Y( d8 P0 B, K
- }
1 ]9 Y! k1 w7 n. h2 x' J0 p7 ~- A2 \
! L! H- L% e/ {) o, x- 8 G; \$ I/ I; j8 R! e4 r
- - (NIMAttributedLabel *)label
' h; A7 T3 u0 |& @+ W: } - {
" O, a" [$ `3 P) l3 x - if (_label) {
- Z7 i2 ~' ?3 [8 a3 v1 A - return _label;+ A) d7 c9 @) \$ h5 l7 j" v8 x
- }7 K2 Y: {% J. S) @1 [( G) P8 c
- _label = [[NIMAttributedLabel alloc] initWithFrame:CGRectZero];
# C# b; J% j2 d, O9 Q - _label.font = [UIFont systemFontOfSize:NIMKit_Message_Font_Size];
1 G' @. b q) j1 m* i) I( ^ - return _label;6 {- Y4 s! C2 K! s
- }8 b: [' g$ R8 f; N' E7 X
- @end
复制代码这里就是实现了NIMSessionContentConfig配置协议,我举例一个文本消息类型的sessionContentView的处理方式,其他类型是一样的处理方法,实现相关配置协议方法即可。你可能会想到如果某个sessionContentView上面的元素有很多时该怎么处理,我该不会把某个sessionContentView的元素都定义一次,然后全部赋值再计算contentSize么?我将会在说自定义消息类型的时候谈谈我简单的处理方式。2 m: o8 P. I: j# b
这里我个人觉得有两点可以改变一下。 - NIMBaseSessionContentConfig的NIMMessage对象应该改为NIMMessageModel对象比较好。因为我需要用到contentSize,根据contentSize来设置控件的宽度适应屏幕。所以我在自定义的消息里面,将NTESCustomAttachmentInfo协议需要传入的NIMMessage对象改为NIMMessageModel对象。
- 在返回contentView类名时,改为NSStringFromClass([NIMSessionTextContentView Class])会好点,怕输入字符串时时产生错误嘛。% ]) `( b, \4 v. l9 j; ~" e1 t
在NIMSessionContentConfigFactory类里面定义了基本消息类型所对应的contentConfig配置协议(注意,在云信demo里面,每个sessionContentView都对应一个sessionContentConfig)。请看NIMUnsupportContentConfig判nil处理,如果没有这段判断处理,你在添加自定义消息时候,忘记在 NTESSessionCustomLayoutConfig类的supportAttachmentType方法里面添加你的自定义消息,程序就会崩溃。ps:防止崩溃,请从细节做起,谢谢! 那NIMCellLayoutDefaultConfig计算contentSize就简单明了了,就是调用相关 sessionContentConfig 的方法嘛。 NNTESSessionCustomContentConfig计算contentSize当我们看到NTESSessionCustomLayoutConfig类时,有两个地方是值得我们注意,也是与NIMCellLayoutDefaultConfig不同的地方。 - 一个NTESSessionCustomContentConfig类的属性
- supportAttachmentType 内部方法,用来获取customLayoutConfig直接的类型。
7 m: _' x8 w+ l: j) g D' H9 O
NTESSessionCustomContentConfig它有一个NIMMessage类型的public属性,而在介绍NIMBaseSessionContentConfig配置协议时,我有说过建议将它的NIMMessage对象改为NIMMessageModel对象,在这里我也同样建议,原因上面有提过。
: K- M6 {, t! b4 f1 Z- l请看它的.m文件:
0 }' @7 p; a) M' X( r7 e - @interface NTESSessionCustomContentConfig()' g$ [) T {# s
- ; F5 X+ K1 [ z3 d
- @property (nonatomic, strong) id attachmentInfo;6 Y& K7 N" g& | M) u" s( V) w9 g, T8 }
/ F- u, \# B$ e- @end2 Y4 o c$ b, \# }( Q$ ~1 @
- }% V; D& m. k6 J) M3 ^
- @implementation NTESSessionCustomContentConfig, \! b; ?9 p' t) `# h
- / Q/ s# L! O- j0 R
- - (void)setMessage:(NIMMessage *)message* \) G+ _) g' ]+ b' q; M
- {; A) F. ^- t2 z6 x& [. s: _
- NIMCustomObject *object = message.messageObject;1 U7 q- _ t/ B! ]2 @
- _message = message;* A, c3 N- Q) A- [' R# x* B
- _attachmentInfo = (id)object.attachment;
/ f3 i# H* \, L+ P3 |: }, [ - }7 G/ N5 u3 c! m; ^3 Z
, g/ g6 ]) e& [- ~: v0 w9 O- - (CGSize)contentSize:(CGFloat)cellWidth
3 K! K& r# V F - {. Y7 Y3 r2 v4 L; r7 n: H2 |- h
- return [self.attachmentInfo contentSize:self.message cellWidth:cellWidth];
) P! t2 y/ h6 o. H) O, r4 U0 b - }
* z$ Y$ M- V; j4 r
8 k+ ~# a) [' V4 p/ p' i# [- - (NSString *)cellContent8 t+ O: N# z- r' T
- {
+ H8 u9 _! D1 V( `2 w, ]( M6 {" G* F - return [self.attachmentInfo cellContent:self.message]; }- L9 D6 g$ D- @5 M# c
- }( v6 T( Q% H' M5 t; N9 t9 @' u
- " [$ o- {& S, s1 H
- - (UIEdgeInsets)contentViewInsets7 U( m) a- p% Y( B
- {
+ B4 C6 Z" P- S e& }4 m - return [self.attachmentInfo contentViewInsets:self.message];
, ^+ `- H6 r3 _7 Z - }, Y# l4 H% D/ a" E$ a
- * J: F1 w: O, y
- @end
复制代码attachmentInfo对象代表不同类型的自定义消息,只要它遵守NTESCustomAttachmentInfo协议即可。(ps:其实NTESCustomAttachmentInfo协议就相当于上面基本消息类型所对应的NIMSessionContentConfig协议;注:这里所谓的基本消息类型,即云信SDK已定义的消息类型,相对于自定义消息类型而言而已。) 下面来看看 NTESCustomAttachmentInfo 协议(已添加注释)。
! ~* O6 B: X1 L- c$ D - @protocol NTESCustomAttachmentInfo " ~/ D" B0 f- i4 X& | U- b* @" `
- n; k6 I/ V# z% {; v M
- @optional3 O/ P! c4 L2 Z h& i
- /// contentView类名2 E. e& F- {- W2 W
- - (NSString *)cellContent:(NIMMessage *)message;
* N1 x1 C/ l5 f% U: d( e! f - /// contentSize
% F* Z o$ V) U* B8 o - - (CGSize)contentSize:(NIMMessage *)message cellWidth:(CGFloat)width;
; P( Q0 f: k7 g3 y4 z2 k - /// 内容距离bubble气泡的相关距离
9 H6 b- \5 I/ i, i6 u - - (UIEdgeInsets)contentViewInsets:(NIMMessage *)message;
l0 o( H. z3 J: z - /// 格式化消息 某些消息需要在最近回话列表特殊文字 如:收到一段文字,但是需要显示[系统消息]6 F8 i$ ?# w! Z0 @$ p- T' z4 Z. ~; g
- - (NSString *)formatedMessage;- H j% H( u) F
- /// 封面图片 如果一个视频 得显示一张图片在界面
3 m [" |0 W: n# b - - (UIImage *)showCoverImage;
$ g3 s1 D+ K4 | - /// 设置封面图片; ~: p0 q' o( b9 s' n
- - (void)setShowCoverImage:(UIImage *)image;
复制代码在这里我提出两点建议 - NIMMessage对象改为NIMMessageModel对象;
- cellContent:、contentSize: cellWidth:、contentViewInsets:这三个方法改为@required类型的;方法名前面加上attachmentInfo与NIMSessionContentConfig协议的相关方法作为区分。
; @3 N' e) }' G0 `
复杂自定义sessionContentView的简单处理方式上面在介绍NIMBaseSessionContentConfig配置协议时,我有提到如果某个sessionContentView上面的元素有很多时该怎么处理。下面我说说我的处理方式。 - 把计算contentSize和contentViewInsets的方法丢到contentView里面,这样一来,那么只要在attachMentInfo里面调用所属contentView的计算方法。
我会在NIMSessionMessageContentView类里面增加两个方法 - - (CGSize)attachmentInfoViewContentSize:(NIMMessageModel *)messageModel cellWidth:(CGFloat)width;2 _# `- p. i; o1 E4 X& j
- - (UIEdgeInsets)attachmentInfoViewcontentViewInsets:(NIMMessageModel *)messageModel;
复制代码
. G3 v3 T. g) x. D2 q v在具体的contentView里面,我定义方法,它有一个Bool类型的isInit(是否初始化)入参,在这个方法里面我创建和实例变量一样的临时变量,当attachmentInfoViewContentSize: cellWidth方法调用它时,我只是为了方便计算contentSize,如果是initSessionMessageContentView方法调用时,我就将相应的临时变量赋值给例变量。
* Q* K( ?* w) f
原文链接:http://joakimliu.github.io/2016/03/27/NIMKit%E6%B5%85%E6%9E%90/ |