NIMSessionRobotContentView.m 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444
  1. //
  2. // NIMSessionRobotContentView.m
  3. // NIMKit
  4. //
  5. // Created by chris on 2017/6/27.
  6. // Copyright © 2017年 NetEase. All rights reserved.
  7. //
  8. #import "NIMSessionRobotContentView.h"
  9. #import "UIView+NIM.h"
  10. #import "NIMKitRobotDefaultTemplateParser.h"
  11. #import "NIMKit.h"
  12. #import "M80AttributedLabel+NIMKit.h"
  13. #import "UIImageView+WebCache.h"
  14. #import "NIMGlobalMacro.h"
  15. #import "NIMKit.h"
  16. @interface NIMSessionRobotButton : UIButton
  17. @property (nonatomic,copy) NSString *target;
  18. @property (nonatomic,copy) NSString *url;
  19. @property (nonatomic,copy) NSString *param;
  20. @property (nonatomic,assign) NIMKitRobotTemplateItemType type;
  21. @end
  22. @interface NIMSessionRobotContentView()
  23. @property (nonatomic,strong) NSMutableSet *buttons;
  24. @property (nonatomic,strong) NSMutableSet *labels;
  25. @property (nonatomic,strong) NSMutableSet *imageViews;
  26. @property (nonatomic,strong) UIButton *continueButton;
  27. @property (nonatomic,strong) NIMKitRobotDefaultTemplateParser *parser;
  28. @end
  29. @implementation NIMSessionRobotContentView
  30. - (instancetype)initSessionMessageContentView
  31. {
  32. self = [super initSessionMessageContentView];
  33. if (self)
  34. {
  35. _buttons = [[NSMutableSet alloc] init];
  36. _labels = [[NSMutableSet alloc] init];
  37. _imageViews = [[NSMutableSet alloc] init];
  38. _parser = [[NIMKitRobotDefaultTemplateParser alloc] init];
  39. _continueButton = [UIButton buttonWithType:UIButtonTypeCustom];
  40. [_continueButton setTitle:@"继续对话" forState:UIControlStateNormal];
  41. [_continueButton setTitleColor:NIMKit_UIColorFromRGB(0x168cf6) forState:UIControlStateNormal];
  42. [_continueButton addTarget:self action:@selector(onContinue:) forControlEvents:UIControlEventTouchUpInside];
  43. }
  44. return self;
  45. }
  46. - (void)refresh:(NIMMessageModel *)data
  47. {
  48. [super refresh:data];
  49. [self setupRobot:data];
  50. }
  51. - (void)onContinue:(UIButton *)button
  52. {
  53. NIMKitEvent *event = [[NIMKitEvent alloc] init];
  54. event.eventName = NIMKitEventNameTapRobotContinueSession;
  55. event.messageModel = self.model;
  56. [self.delegate onCatchEvent:event];
  57. }
  58. - (void)refreshContineButton:(NIMMessageModel *)data
  59. {
  60. if (data.message.session.sessionType == NIMSessionTypeTeam)
  61. {
  62. [self addSubview:self.continueButton];
  63. }
  64. else
  65. {
  66. [self.continueButton removeFromSuperview];
  67. }
  68. NIMKitSetting *setting = [[NIMKit sharedKit].config setting:data.message];
  69. self.continueButton.titleLabel.font = setting.font;
  70. [self.continueButton sizeToFit];
  71. }
  72. - (void)setupRobot:(NIMMessageModel *)data
  73. {
  74. //上行消息交给 TextContentView 处理了
  75. //一定是机器人下行消息
  76. [self recycleAllSubViews:self];
  77. NIMKitRobotTemplate *template = [[NIMKit sharedKit].robotTemplateParser robotTemplate:data.message];
  78. if (![template.version isEqualToString:@"0.1"])
  79. {
  80. NSLog(@"robot template version incompatible!");
  81. }
  82. for (NIMKitRobotTemplateLayout *layout in template.items)
  83. {
  84. for (NIMKitRobotTemplateItem *item in layout.items)
  85. {
  86. [self applyItem:item inView:self];
  87. }
  88. }
  89. [self refreshContineButton:data];
  90. }
  91. - (void)applyItem:(NIMKitRobotTemplateItem *)item
  92. inView:(UIView *)view
  93. {
  94. switch (item.itemType) {
  95. case NIMKitRobotTemplateItemTypeText:
  96. {
  97. M80AttributedLabel *label = [self genLabel];
  98. label.text = item.content;
  99. if ([view isKindOfClass:[UIButton class]])
  100. {
  101. // button 里头的 title 全部居中
  102. label.textAlignment = kCTTextAlignmentCenter;
  103. label.textColor = NIMKit_UIColorFromRGB(0x248DFA);
  104. }
  105. [view addSubview:label];
  106. }
  107. break;
  108. case NIMKitRobotTemplateItemTypeImage:
  109. {
  110. UIImageView *imageView = [self genImageView];
  111. imageView.nim_size = CGSizeMake(item.width.floatValue, item.height.floatValue);
  112. imageView.image = nil;
  113. if (item.url.length)
  114. {
  115. [imageView sd_setImageWithURL:[NSURL URLWithString:[[NIMSDK sharedSDK].resourceManager normalizeURLString:item.thumbUrl]] placeholderImage:nil options:SDWebImageRetryFailed];
  116. }
  117. [view addSubview:imageView];
  118. }
  119. break;
  120. case NIMKitRobotTemplateItemTypeLinkURL:
  121. case NIMKitRobotTemplateItemTypeLinkBlock:
  122. {
  123. NIMSessionRobotButton *button = [self genButton];
  124. NIMKitRobotTemplateLinkItem *link = (NIMKitRobotTemplateLinkItem *)item;
  125. button.target = link.target;
  126. button.param = link.params;
  127. button.url = link.url;
  128. button.type = link.itemType;
  129. for (NIMKitRobotTemplateItem *linkItem in link.items)
  130. {
  131. [self applyItem:linkItem inView:button];
  132. }
  133. [view addSubview:button];
  134. }
  135. break;
  136. default:
  137. break;
  138. }
  139. }
  140. - (CGSize)sizeThatFits:(CGSize)size
  141. {
  142. CGFloat height = [NIMSessionRobotContentView itemSpacing];
  143. CGFloat width = 0;;
  144. for (UIView *view in self.subviews) {
  145. if (view == self.bubbleImageView)
  146. {
  147. continue;
  148. }
  149. width = MAX(width, view.nim_width);
  150. height += view.nim_height;
  151. if (view == self.continueButton)
  152. {
  153. height += [NIMSessionRobotContentView continueItemSpacing];
  154. }
  155. else
  156. {
  157. height += [NIMSessionRobotContentView itemSpacing];
  158. }
  159. }
  160. return CGSizeMake(width, height);
  161. }
  162. - (void)onTouchButton:(NIMSessionRobotButton *)button
  163. {
  164. NIMKitEvent *event = [[NIMKitEvent alloc] init];
  165. if (button.type == NIMKitRobotTemplateItemTypeLinkURL)
  166. {
  167. event.eventName = NIMKitEventNameTapRobotLink;
  168. event.data = button.target;
  169. }
  170. else
  171. {
  172. event.eventName = NIMKitEventNameTapRobotBlock;
  173. NSMutableDictionary *data = [@{@"target":button.target} mutableCopy];
  174. NIMRobotObject *object = (NIMRobotObject *)self.model.message.messageObject;
  175. [data setObject:object.robotId forKey:@"robotId"];
  176. if (button.param)
  177. {
  178. [data setObject:button.param forKey:@"param"];
  179. }
  180. NSString *text = @"";
  181. for (M80AttributedLabel *label in button.subviews)
  182. {
  183. if ([label isKindOfClass:[M80AttributedLabel class]])
  184. {
  185. text = [text stringByAppendingFormat:@"%@ ",label.text];
  186. }
  187. }
  188. [data setObject:text forKey:@"text"];
  189. event.data = data;
  190. }
  191. [self.delegate onCatchEvent:event];
  192. }
  193. - (void)layoutSubviews
  194. {
  195. [super layoutSubviews];
  196. [self resizeAllSubView:self insets:self.model.contentViewInsets];
  197. CGFloat top = [NIMSessionRobotContentView itemSpacing];
  198. for (UIView *view in self.subviews)
  199. {
  200. if (view == self.bubbleImageView)
  201. {
  202. continue;
  203. }
  204. if (view == self.continueButton)
  205. {
  206. CGFloat rightMargin = 16.f;
  207. self.continueButton.nim_right = self.nim_width - rightMargin;
  208. self.continueButton.nim_bottom = self.nim_height;
  209. continue;
  210. }
  211. view.nim_left = self.model.contentViewInsets.left;
  212. view.nim_top = top;
  213. top = view.nim_bottom + [NIMSessionRobotContentView itemSpacing];
  214. }
  215. }
  216. - (void)resizeAllSubView:(UIView *)superView insets:(UIEdgeInsets)insets
  217. {
  218. CGFloat width = superView.nim_width - insets.left - insets.right;
  219. for (UIView *subView in superView.subviews)
  220. {
  221. if (subView.nim_height == 0)
  222. {
  223. if ([subView isKindOfClass:[M80AttributedLabel class]])
  224. {
  225. M80AttributedLabel *label = (M80AttributedLabel *)subView;
  226. CGSize size = [label sizeThatFits:CGSizeMake(width, CGFLOAT_MAX)];
  227. label.nim_size = CGSizeMake(size.width, size.height);
  228. [self resizeAllSubView:label insets:UIEdgeInsetsZero];
  229. }
  230. else if ([subView isKindOfClass:[UIImageView class]])
  231. {
  232. UIImageView *imageView = (UIImageView *)subView;
  233. CGFloat defaultImageWidth = 75.f;
  234. CGFloat defaultImageHeight = 75.f;
  235. imageView.nim_size = CGSizeMake(defaultImageWidth, defaultImageHeight);
  236. [self resizeAllSubView:imageView insets:UIEdgeInsetsZero];
  237. }
  238. else if ([subView isKindOfClass:[NIMSessionRobotButton class]])
  239. {
  240. NIMSessionRobotButton *button = (NIMSessionRobotButton *)subView;
  241. button.nim_width = width;
  242. [self resizeAllSubView:button insets:UIEdgeInsetsZero];
  243. [button sizeToFit];
  244. }
  245. }
  246. }
  247. }
  248. - (void)recycleAllSubViews:(UIView *)view
  249. {
  250. for (UIView *subView in view.subviews)
  251. {
  252. if (subView == self.bubbleImageView || subView == self.continueButton) {
  253. continue;
  254. }
  255. [subView removeFromSuperview];
  256. subView.frame = CGRectZero;
  257. if ([subView isKindOfClass:[NIMSessionRobotButton class]])
  258. {
  259. NIMSessionRobotButton *btn = (NIMSessionRobotButton *)subView;
  260. btn.target = nil;
  261. btn.url = nil;
  262. btn.param = nil;
  263. [btn removeTarget:self action:nil forControlEvents:UIControlEventTouchUpInside];
  264. [_buttons addObject:subView];
  265. }
  266. if ([subView isKindOfClass:[UILabel class]])
  267. {
  268. [_labels addObject:subView];
  269. }
  270. if ([subView isKindOfClass:[UIImageView class]])
  271. {
  272. [_imageViews addObject:subView];
  273. }
  274. [self recycleAllSubViews:subView];
  275. }
  276. }
  277. - (M80AttributedLabel *)genLabel
  278. {
  279. M80AttributedLabel *label = nil;
  280. if (self.labels.count)
  281. {
  282. label = self.labels.anyObject;
  283. [self.labels removeObject:label];
  284. }
  285. else
  286. {
  287. label = [[M80AttributedLabel alloc] initWithFrame:CGRectZero];
  288. label.numberOfLines = 0;
  289. label.lineBreakMode = NSLineBreakByWordWrapping;
  290. label.textAlignment = kCTTextAlignmentLeft;
  291. label.backgroundColor = [UIColor clearColor];
  292. label.autoresizingMask = UIViewAutoresizingFlexibleWidth;
  293. }
  294. NIMKitSetting *setting = [[NIMKit sharedKit].config setting:self.model.message];
  295. label.textColor = setting.textColor;
  296. label.font = setting.font;
  297. return label;
  298. }
  299. - (NIMSessionRobotButton *)genButton
  300. {
  301. NIMSessionRobotButton *button = nil;
  302. if (self.buttons.count)
  303. {
  304. button = self.buttons.anyObject;
  305. [self.buttons removeObject:button];
  306. }
  307. else
  308. {
  309. button = [[NIMSessionRobotButton alloc] initWithFrame:CGRectMake(0, 0, 0, 0)];
  310. button.autoresizingMask = UIViewAutoresizingFlexibleWidth;
  311. }
  312. [button addTarget:self action:@selector(onTouchButton:) forControlEvents:UIControlEventTouchUpInside];
  313. return button;
  314. }
  315. - (UIImageView *)genImageView
  316. {
  317. UIImageView *view = nil;
  318. if (self.imageViews.count)
  319. {
  320. view = self.imageViews.anyObject;
  321. [self.imageViews removeObject:view];
  322. }
  323. else
  324. {
  325. view = [[UIImageView alloc] initWithFrame:CGRectZero];
  326. }
  327. return view;
  328. }
  329. + (CGFloat)itemSpacing
  330. {
  331. return 11.f;
  332. }
  333. + (CGFloat)continueItemSpacing
  334. {
  335. return 5.f;
  336. }
  337. @end
  338. @implementation NIMSessionRobotButton
  339. - (instancetype)initWithFrame:(CGRect)frame
  340. {
  341. self = [super initWithFrame:frame];
  342. if (self)
  343. {
  344. self.layer.borderColor = NIMKit_UIColorFromRGB(0x248DFA).CGColor;
  345. self.layer.borderWidth = 1.f;
  346. self.layer.cornerRadius = 22;
  347. }
  348. return self;
  349. }
  350. - (CGSize)sizeThatFits:(CGSize)size
  351. {
  352. CGFloat height = [NIMSessionRobotContentView itemSpacing];
  353. for (UIView *subView in self.subviews) {
  354. height += subView.nim_height;
  355. height += [NIMSessionRobotContentView itemSpacing];
  356. }
  357. return CGSizeMake(self.nim_width, height);
  358. }
  359. - (void)setHighlighted:(BOOL)highlighted
  360. {
  361. [super setHighlighted:highlighted];
  362. self.alpha = highlighted? 0.5f : 1.0f;
  363. }
  364. - (void)layoutSubviews
  365. {
  366. [super layoutSubviews];
  367. CGFloat top = [NIMSessionRobotContentView itemSpacing];
  368. for (UIView *view in self.subviews)
  369. {
  370. view.nim_centerX = self.nim_width * .5;
  371. view.nim_top = top;
  372. top = view.nim_bottom + [NIMSessionRobotContentView itemSpacing];
  373. }
  374. }
  375. @end