EaseChatToolbar.m 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908
  1. /************************************************************
  2. * * Hyphenate CONFIDENTIAL
  3. * __________________
  4. * Copyright (C) 2016 Hyphenate Inc. All rights reserved.
  5. *
  6. * NOTICE: All information contained herein is, and remains
  7. * the property of Hyphenate Inc.
  8. * Dissemination of this information or reproduction of this material
  9. * is strictly forbidden unless prior written permission is obtained
  10. * from Hyphenate Inc.
  11. */
  12. #import "EaseChatToolbar.h"
  13. #import "EaseFaceView.h"
  14. #import "EaseEmoji.h"
  15. #import "EaseEmotionEscape.h"
  16. #import "EaseEmotionManager.h"
  17. #import "EaseLocalDefine.h"
  18. @interface EaseChatToolbar()<UITextViewDelegate, EMFaceDelegate>
  19. @property (nonatomic) CGFloat version;
  20. @property (strong, nonatomic) NSMutableArray *leftItems;
  21. @property (strong, nonatomic) NSMutableArray *rightItems;
  22. @property (strong, nonatomic) UIImageView *toolbarBackgroundImageView;
  23. @property (strong, nonatomic) UIImageView *backgroundImageView;
  24. @property (nonatomic) BOOL isShowButtomView;
  25. @property (strong, nonatomic) UIView *activityButtomView;
  26. @property (strong, nonatomic) UIView *toolbarView;
  27. @property (strong, nonatomic) UIButton *recordButton;
  28. @property (strong, nonatomic) UIButton *moreButton;
  29. @property (strong, nonatomic) UIButton *faceButton;
  30. @property (nonatomic) CGFloat previousTextViewContentHeight;//上一次inputTextView的contentSize.height
  31. @property (nonatomic) NSLayoutConstraint *inputViewWidthItemsLeftConstraint;
  32. @property (nonatomic) NSLayoutConstraint *inputViewWidthoutItemsLeftConstraint;
  33. @end
  34. @implementation EaseChatToolbar
  35. @synthesize faceView = _faceView;
  36. @synthesize moreView = _moreView;
  37. @synthesize recordView = _recordView;
  38. - (instancetype)initWithFrame:(CGRect)frame
  39. {
  40. self = [self initWithFrame:frame horizontalPadding:8 verticalPadding:5 inputViewMinHeight:36 inputViewMaxHeight:150 type:EMChatToolbarTypeGroup];
  41. if (self) {
  42. }
  43. return self;
  44. }
  45. - (instancetype)initWithFrame:(CGRect)frame
  46. type:(EMChatToolbarType)type
  47. {
  48. self = [self initWithFrame:frame horizontalPadding:8 verticalPadding:5 inputViewMinHeight:36 inputViewMaxHeight:150 type:type];
  49. if (self) {
  50. }
  51. return self;
  52. }
  53. - (instancetype)initWithFrame:(CGRect)frame
  54. horizontalPadding:(CGFloat)horizontalPadding
  55. verticalPadding:(CGFloat)verticalPadding
  56. inputViewMinHeight:(CGFloat)inputViewMinHeight
  57. inputViewMaxHeight:(CGFloat)inputViewMaxHeight
  58. type:(EMChatToolbarType)type
  59. {
  60. if (frame.size.height < (verticalPadding * 2 + inputViewMinHeight)) {
  61. frame.size.height = verticalPadding * 2 + inputViewMinHeight;
  62. }
  63. self = [super initWithFrame:frame];
  64. if (self) {
  65. self.accessibilityIdentifier = @"chatbar";
  66. _horizontalPadding = horizontalPadding;
  67. _verticalPadding = verticalPadding;
  68. _inputViewMinHeight = inputViewMinHeight;
  69. _inputViewMaxHeight = inputViewMaxHeight;
  70. _chatBarType = type;
  71. _leftItems = [NSMutableArray array];
  72. _rightItems = [NSMutableArray array];
  73. _version = [[[UIDevice currentDevice] systemVersion] floatValue];
  74. _activityButtomView = nil;
  75. _isShowButtomView = NO;
  76. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(chatKeyboardWillChangeFrame:) name:UIKeyboardWillChangeFrameNotification object:nil];
  77. [self _setupSubviews];
  78. }
  79. return self;
  80. }
  81. #pragma mark - setup subviews
  82. /*!
  83. @method
  84. @brief 加载视图
  85. @discussion
  86. @result
  87. */
  88. - (void)_setupSubviews
  89. {
  90. //backgroundImageView
  91. _backgroundImageView = [[UIImageView alloc] initWithFrame:self.bounds];
  92. _backgroundImageView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
  93. _backgroundImageView.backgroundColor = [UIColor clearColor];
  94. _backgroundImageView.image = [[UIImage imageNamed:@"EaseUIResource.bundle/messageToolbarBg"] stretchableImageWithLeftCapWidth:0.5 topCapHeight:10];
  95. [self addSubview:_backgroundImageView];
  96. //toolbar
  97. _toolbarView = [[UIView alloc] initWithFrame:self.bounds];
  98. _toolbarView.backgroundColor = [UIColor clearColor];
  99. [self addSubview:_toolbarView];
  100. _toolbarBackgroundImageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, _toolbarView.frame.size.width, _toolbarView.frame.size.height)];
  101. _toolbarBackgroundImageView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
  102. _toolbarBackgroundImageView.backgroundColor = [UIColor clearColor];
  103. [_toolbarView addSubview:_toolbarBackgroundImageView];
  104. //input textview
  105. _inputTextView = [[EaseTextView alloc] initWithFrame:CGRectMake(self.horizontalPadding, self.verticalPadding, self.frame.size.width - self.verticalPadding * 2, self.frame.size.height - self.verticalPadding * 2)];
  106. _inputTextView.autoresizingMask = UIViewAutoresizingFlexibleHeight;
  107. _inputTextView.scrollEnabled = YES;
  108. _inputTextView.returnKeyType = UIReturnKeySend;
  109. _inputTextView.enablesReturnKeyAutomatically = YES; // UITextView内部判断send按钮是否可以用
  110. _inputTextView.placeHolder = NSEaseLocalizedString(@"message.toolBar.inputPlaceHolder", @"input a new message");
  111. _inputTextView.delegate = self;
  112. _inputTextView.backgroundColor = [UIColor clearColor];
  113. _inputTextView.layer.borderColor = [UIColor colorWithWhite:0.8f alpha:1.0f].CGColor;
  114. _inputTextView.layer.borderWidth = 0.65f;
  115. _inputTextView.layer.cornerRadius = 6.0f;
  116. _previousTextViewContentHeight = [self _getTextViewContentH:_inputTextView];
  117. [_toolbarView addSubview:_inputTextView];
  118. //change input type
  119. UIButton *styleChangeButton = [[UIButton alloc] init];
  120. styleChangeButton.accessibilityIdentifier = @"style";
  121. styleChangeButton.autoresizingMask = UIViewAutoresizingFlexibleTopMargin;
  122. [styleChangeButton setImage:[UIImage imageNamed:@"EaseUIResource.bundle/chatBar_record"] forState:UIControlStateNormal];
  123. [styleChangeButton setImage:[UIImage imageNamed:@"EaseUIResource.bundle/chatBar_keyboard"] forState:UIControlStateSelected];
  124. [styleChangeButton addTarget:self action:@selector(styleButtonAction:) forControlEvents:UIControlEventTouchUpInside];
  125. EaseChatToolbarItem *styleItem = [[EaseChatToolbarItem alloc] initWithButton:styleChangeButton withView:nil];
  126. [self setInputViewLeftItems:@[styleItem]];
  127. //record
  128. self.recordButton = [[UIButton alloc] initWithFrame:self.inputTextView.frame];
  129. self.recordButton.accessibilityIdentifier = @"record";
  130. self.recordButton.titleLabel.font = [UIFont systemFontOfSize:15.0];
  131. [self.recordButton setTitleColor:[UIColor darkGrayColor] forState:UIControlStateNormal];
  132. [self.recordButton setBackgroundImage:[[UIImage imageNamed:@"EaseUIResource.bundle/chatBar_recordBg"] stretchableImageWithLeftCapWidth:10 topCapHeight:10] forState:UIControlStateNormal];
  133. [self.recordButton setBackgroundImage:[[UIImage imageNamed:@"EaseUIResource.bundle/chatBar_recordSelectedBg"] stretchableImageWithLeftCapWidth:10 topCapHeight:10] forState:UIControlStateHighlighted];
  134. [self.recordButton setTitle:kTouchToRecord forState:UIControlStateNormal];
  135. [self.recordButton setTitle:kTouchToFinish forState:UIControlStateHighlighted];
  136. self.recordButton.hidden = YES;
  137. [self.recordButton addTarget:self action:@selector(recordButtonTouchDown) forControlEvents:UIControlEventTouchDown];
  138. [self.recordButton addTarget:self action:@selector(recordButtonTouchUpOutside) forControlEvents:UIControlEventTouchUpOutside];
  139. [self.recordButton addTarget:self action:@selector(recordButtonTouchUpInside) forControlEvents:UIControlEventTouchUpInside];
  140. [self.recordButton addTarget:self action:@selector(recordDragOutside) forControlEvents:UIControlEventTouchDragExit];
  141. [self.recordButton addTarget:self action:@selector(recordDragInside) forControlEvents:UIControlEventTouchDragEnter];
  142. self.recordButton.hidden = YES;
  143. [self.toolbarView addSubview:self.recordButton];
  144. //emoji
  145. self.faceButton = [[UIButton alloc] init];
  146. self.faceButton.accessibilityIdentifier = @"face";
  147. self.faceButton.autoresizingMask = UIViewAutoresizingFlexibleTopMargin;
  148. [self.faceButton setImage:[UIImage imageNamed:@"EaseUIResource.bundle/chatBar_face"] forState:UIControlStateNormal];
  149. [self.faceButton setImage:[UIImage imageNamed:@"EaseUIResource.bundle/chatBar_faceSelected"] forState:UIControlStateHighlighted];
  150. [self.faceButton setImage:[UIImage imageNamed:@"EaseUIResource.bundle/chatBar_keyboard"] forState:UIControlStateSelected];
  151. [self.faceButton addTarget:self action:@selector(faceButtonAction:) forControlEvents:UIControlEventTouchUpInside];
  152. EaseChatToolbarItem *faceItem = [[EaseChatToolbarItem alloc] initWithButton:self.faceButton withView:self.faceView];
  153. //more
  154. self.moreButton = [[UIButton alloc] init];
  155. self.moreButton.accessibilityIdentifier = @"more";
  156. self.moreButton.autoresizingMask = UIViewAutoresizingFlexibleTopMargin;
  157. [self.moreButton setImage:[UIImage imageNamed:@"EaseUIResource.bundle/chatBar_more"] forState:UIControlStateNormal];
  158. [self.moreButton setImage:[UIImage imageNamed:@"EaseUIResource.bundle/chatBar_moreSelected"] forState:UIControlStateHighlighted];
  159. [self.moreButton setImage:[UIImage imageNamed:@"EaseUIResource.bundle/chatBar_keyboard"] forState:UIControlStateSelected];
  160. [self.moreButton addTarget:self action:@selector(moreButtonAction:) forControlEvents:UIControlEventTouchUpInside];
  161. EaseChatToolbarItem *moreItem = [[EaseChatToolbarItem alloc] initWithButton:self.moreButton withView:self.moreView];
  162. [self setInputViewRightItems:@[faceItem, moreItem]];
  163. }
  164. - (void)dealloc
  165. {
  166. [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillChangeFrameNotification object:nil];
  167. _delegate = nil;
  168. _inputTextView.delegate = nil;
  169. _inputTextView = nil;
  170. }
  171. #pragma mark - getter
  172. - (UIView *)recordView
  173. {
  174. if (_recordView == nil) {
  175. _recordView = [[EaseRecordView alloc] initWithFrame:CGRectMake(90, 130, 140, 140)];
  176. }
  177. return _recordView;
  178. }
  179. - (UIView *)faceView
  180. {
  181. if (_faceView == nil) {
  182. _faceView = [[EaseFaceView alloc] initWithFrame:CGRectMake(0, CGRectGetMaxY(_toolbarView.frame), self.frame.size.width, 180)];
  183. [(EaseFaceView *)_faceView setDelegate:self];
  184. _faceView.backgroundColor = [UIColor colorWithRed:240 / 255.0 green:242 / 255.0 blue:247 / 255.0 alpha:1.0];
  185. _faceView.autoresizingMask = UIViewAutoresizingFlexibleTopMargin;
  186. }
  187. return _faceView;
  188. }
  189. - (UIView *)moreView
  190. {
  191. if (_moreView == nil) {
  192. _moreView = [[EaseChatBarMoreView alloc] initWithFrame:CGRectMake(0, CGRectGetMaxY(_toolbarView.frame), self.frame.size.width, 80) type:self.chatBarType];
  193. _moreView.backgroundColor = [UIColor colorWithRed:240 / 255.0 green:242 / 255.0 blue:247 / 255.0 alpha:1.0];
  194. _moreView.autoresizingMask = UIViewAutoresizingFlexibleTopMargin;
  195. }
  196. return _moreView;
  197. }
  198. #pragma mark - setter
  199. - (void)setDelegate:(id)delegate
  200. {
  201. _delegate = delegate;
  202. if ([_moreView isKindOfClass:[EaseChatBarMoreView class]]) {
  203. [(EaseChatBarMoreView *)_moreView setDelegate:delegate];
  204. }
  205. }
  206. - (void)setRecordView:(UIView *)recordView
  207. {
  208. if(_recordView != recordView){
  209. _recordView = recordView;
  210. }
  211. }
  212. - (void)setMoreView:(UIView *)moreView
  213. {
  214. if (_moreView != moreView) {
  215. _moreView = moreView;
  216. for (EaseChatToolbarItem *item in self.rightItems) {
  217. if (item.button == self.moreButton) {
  218. item.button2View = _moreView;
  219. break;
  220. }
  221. }
  222. }
  223. }
  224. - (void)setFaceView:(UIView *)faceView
  225. {
  226. if (_faceView != faceView) {
  227. _faceView = faceView;
  228. for (EaseChatToolbarItem *item in self.rightItems) {
  229. if (item.button == self.faceButton) {
  230. item.button2View = _faceView;
  231. break;
  232. }
  233. }
  234. }
  235. }
  236. - (NSArray*)inputViewLeftItems
  237. {
  238. return self.leftItems;
  239. }
  240. /*!
  241. @method
  242. @brief 设置toolBar左侧菜单选项
  243. @discussion
  244. @param inputViewLeftItems 左侧选项
  245. @result
  246. */
  247. - (void)setInputViewLeftItems:(NSArray *)inputViewLeftItems
  248. {
  249. for (EaseChatToolbarItem *item in self.leftItems) {
  250. [item.button removeFromSuperview];
  251. [item.button2View removeFromSuperview];
  252. }
  253. [self.leftItems removeAllObjects];
  254. CGFloat oX = self.horizontalPadding;
  255. CGFloat itemHeight = self.toolbarView.frame.size.height - self.verticalPadding * 2;
  256. for (id item in inputViewLeftItems) {
  257. if ([item isKindOfClass:[EaseChatToolbarItem class]]) {
  258. EaseChatToolbarItem *chatItem = (EaseChatToolbarItem *)item;
  259. if (chatItem.button) {
  260. CGRect itemFrame = chatItem.button.frame;
  261. if (itemFrame.size.height == 0) {
  262. itemFrame.size.height = itemHeight;
  263. }
  264. if (itemFrame.size.width == 0) {
  265. itemFrame.size.width = itemFrame.size.height;
  266. }
  267. itemFrame.origin.x = oX;
  268. itemFrame.origin.y = (self.toolbarView.frame.size.height - itemFrame.size.height) / 2;
  269. chatItem.button.frame = itemFrame;
  270. oX += (itemFrame.size.width + self.horizontalPadding);
  271. [self.toolbarView addSubview:chatItem.button];
  272. [self.leftItems addObject:chatItem];
  273. }
  274. }
  275. }
  276. CGRect inputFrame = self.inputTextView.frame;
  277. CGFloat value = inputFrame.origin.x - oX;
  278. inputFrame.origin.x = oX;
  279. inputFrame.size.width += value;
  280. self.inputTextView.frame = inputFrame;
  281. CGRect recordFrame = self.recordButton.frame;
  282. recordFrame.origin.x = inputFrame.origin.x;
  283. recordFrame.size.width = inputFrame.size.width;
  284. self.recordButton.frame = recordFrame;
  285. }
  286. - (NSArray*)inputViewRightItems
  287. {
  288. return self.rightItems;
  289. }
  290. /*!
  291. @method
  292. @brief 设置toolBar右侧菜单选项
  293. @discussion
  294. @param inputViewLeftItems 右侧选项
  295. @result
  296. */
  297. - (void)setInputViewRightItems:(NSArray *)inputViewRightItems
  298. {
  299. for (EaseChatToolbarItem *item in self.rightItems) {
  300. [item.button removeFromSuperview];
  301. [item.button2View removeFromSuperview];
  302. }
  303. [self.rightItems removeAllObjects];
  304. CGFloat oMaxX = self.toolbarView.frame.size.width - self.horizontalPadding;
  305. CGFloat itemHeight = self.toolbarView.frame.size.height - self.verticalPadding * 2;
  306. if ([inputViewRightItems count] > 0) {
  307. for (NSInteger i = (inputViewRightItems.count - 1); i >= 0; i--) {
  308. id item = [inputViewRightItems objectAtIndex:i];
  309. if ([item isKindOfClass:[EaseChatToolbarItem class]]) {
  310. EaseChatToolbarItem *chatItem = (EaseChatToolbarItem *)item;
  311. if (chatItem.button) {
  312. CGRect itemFrame = chatItem.button.frame;
  313. if (itemFrame.size.height == 0) {
  314. itemFrame.size.height = itemHeight;
  315. }
  316. if (itemFrame.size.width == 0) {
  317. itemFrame.size.width = itemFrame.size.height;
  318. }
  319. oMaxX -= itemFrame.size.width;
  320. itemFrame.origin.x = oMaxX;
  321. itemFrame.origin.y = (self.toolbarView.frame.size.height - itemFrame.size.height) / 2;
  322. chatItem.button.frame = itemFrame;
  323. oMaxX -= self.horizontalPadding;
  324. [self.toolbarView addSubview:chatItem.button];
  325. [self.rightItems addObject:item];
  326. }
  327. }
  328. }
  329. }
  330. CGRect inputFrame = self.inputTextView.frame;
  331. CGFloat value = oMaxX - CGRectGetMaxX(inputFrame);
  332. inputFrame.size.width += value;
  333. self.inputTextView.frame = inputFrame;
  334. CGRect recordFrame = self.recordButton.frame;
  335. recordFrame.origin.x = inputFrame.origin.x;
  336. recordFrame.size.width = inputFrame.size.width;
  337. self.recordButton.frame = recordFrame;
  338. }
  339. #pragma mark - private input view
  340. /*!
  341. @method
  342. @brief 获取textView的高度(实际为textView的contentSize的高度)
  343. @discussion
  344. @param textView 文本框
  345. @result
  346. */
  347. - (CGFloat)_getTextViewContentH:(UITextView *)textView
  348. {
  349. if (self.version >= 7.0)
  350. {
  351. return ceilf([textView sizeThatFits:textView.frame.size].height);
  352. } else {
  353. return textView.contentSize.height;
  354. }
  355. }
  356. /*!
  357. @method
  358. @brief 通过传入的toHeight,跳转toolBar的高度
  359. @discussion
  360. @param toHeight
  361. @result
  362. */
  363. - (void)_willShowInputTextViewToHeight:(CGFloat)toHeight
  364. {
  365. if (toHeight < self.inputViewMinHeight) {
  366. toHeight = self.inputViewMinHeight;
  367. }
  368. if (toHeight > self.inputViewMaxHeight) {
  369. toHeight = self.inputViewMaxHeight;
  370. }
  371. if (toHeight == _previousTextViewContentHeight)
  372. {
  373. return;
  374. }
  375. else{
  376. CGFloat changeHeight = toHeight - _previousTextViewContentHeight;
  377. CGRect rect = self.frame;
  378. rect.size.height += changeHeight;
  379. rect.origin.y -= changeHeight;
  380. self.frame = rect;
  381. rect = self.toolbarView.frame;
  382. rect.size.height += changeHeight;
  383. self.toolbarView.frame = rect;
  384. if (self.version < 7.0) {
  385. [self.inputTextView setContentOffset:CGPointMake(0.0f, (self.inputTextView.contentSize.height - self.inputTextView.frame.size.height) / 2) animated:YES];
  386. }
  387. _previousTextViewContentHeight = toHeight;
  388. if (_delegate && [_delegate respondsToSelector:@selector(chatToolbarDidChangeFrameToHeight:)]) {
  389. [_delegate chatToolbarDidChangeFrameToHeight:self.frame.size.height];
  390. }
  391. }
  392. }
  393. #pragma mark - private bottom view
  394. /*!
  395. @method
  396. @brief 调整toolBar的高度
  397. @discussion
  398. @param bottomHeight 底部菜单的高度
  399. @result
  400. */
  401. - (void)_willShowBottomHeight:(CGFloat)bottomHeight
  402. {
  403. CGRect fromFrame = self.frame;
  404. CGFloat toHeight = self.toolbarView.frame.size.height + bottomHeight;
  405. CGRect toFrame = CGRectMake(fromFrame.origin.x, fromFrame.origin.y + (fromFrame.size.height - toHeight), fromFrame.size.width, toHeight);
  406. if(bottomHeight == 0 && self.frame.size.height == self.toolbarView.frame.size.height)
  407. {
  408. return;
  409. }
  410. if (bottomHeight == 0) {
  411. self.isShowButtomView = NO;
  412. }
  413. else{
  414. self.isShowButtomView = YES;
  415. }
  416. self.frame = toFrame;
  417. if (_delegate && [_delegate respondsToSelector:@selector(chatToolbarDidChangeFrameToHeight:)]) {
  418. [_delegate chatToolbarDidChangeFrameToHeight:toHeight];
  419. }
  420. }
  421. /*!
  422. @method
  423. @brief 切换菜单视图
  424. @discussion
  425. @param bottomView 菜单视图
  426. @result
  427. */
  428. - (void)_willShowBottomView:(UIView *)bottomView
  429. {
  430. if (![self.activityButtomView isEqual:bottomView]) {
  431. CGFloat bottomHeight = bottomView ? bottomView.frame.size.height : 0;
  432. [self _willShowBottomHeight:bottomHeight];
  433. if (bottomView) {
  434. CGRect rect = bottomView.frame;
  435. rect.origin.y = CGRectGetMaxY(self.toolbarView.frame);
  436. bottomView.frame = rect;
  437. [self addSubview:bottomView];
  438. }
  439. if (self.activityButtomView) {
  440. [self.activityButtomView removeFromSuperview];
  441. }
  442. self.activityButtomView = bottomView;
  443. }
  444. }
  445. - (void)_willShowKeyboardFromFrame:(CGRect)beginFrame toFrame:(CGRect)toFrame
  446. {
  447. if (beginFrame.origin.y == [[UIScreen mainScreen] bounds].size.height)
  448. {
  449. [self _willShowBottomHeight:toFrame.size.height];
  450. if (self.activityButtomView) {
  451. [self.activityButtomView removeFromSuperview];
  452. }
  453. self.activityButtomView = nil;
  454. }
  455. else if (toFrame.origin.y == [[UIScreen mainScreen] bounds].size.height)
  456. {
  457. [self _willShowBottomHeight:0];
  458. }
  459. else{
  460. [self _willShowBottomHeight:toFrame.size.height];
  461. }
  462. }
  463. #pragma mark - UITextViewDelegate
  464. - (BOOL)textViewShouldBeginEditing:(UITextView *)textView
  465. {
  466. if ([self.delegate respondsToSelector:@selector(inputTextViewWillBeginEditing:)]) {
  467. [self.delegate inputTextViewWillBeginEditing:self.inputTextView];
  468. }
  469. for (EaseChatToolbarItem *item in self.leftItems) {
  470. item.button.selected = NO;
  471. }
  472. for (EaseChatToolbarItem *item in self.rightItems) {
  473. item.button.selected = NO;
  474. }
  475. return YES;
  476. }
  477. - (void)textViewDidBeginEditing:(UITextView *)textView
  478. {
  479. [textView becomeFirstResponder];
  480. if ([self.delegate respondsToSelector:@selector(inputTextViewDidBeginEditing:)]) {
  481. [self.delegate inputTextViewDidBeginEditing:self.inputTextView];
  482. }
  483. }
  484. - (void)textViewDidEndEditing:(UITextView *)textView
  485. {
  486. [textView resignFirstResponder];
  487. }
  488. - (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
  489. {
  490. if ([text isEqualToString:@"\n"]) {
  491. if ([self.delegate respondsToSelector:@selector(didSendText:)]) {
  492. [self.delegate didSendText:textView.text];
  493. self.inputTextView.text = @"";
  494. [self _willShowInputTextViewToHeight:[self _getTextViewContentH:self.inputTextView]];
  495. }
  496. return NO;
  497. }
  498. else if ([text isEqualToString:@"@"]) {
  499. if ([self.delegate respondsToSelector:@selector(didInputAtInLocation:)]) {
  500. if ([self.delegate didInputAtInLocation:range.location]) {
  501. [self _willShowInputTextViewToHeight:[self _getTextViewContentH:self.inputTextView]];
  502. return NO;
  503. }
  504. }
  505. }
  506. else if ([text length] == 0) {
  507. //delete one character
  508. if (range.length == 1 && [self.delegate respondsToSelector:@selector(didDeleteCharacterFromLocation:)]) {
  509. return ![self.delegate didDeleteCharacterFromLocation:range.location];
  510. }
  511. }
  512. return YES;
  513. }
  514. - (void)textViewDidChange:(UITextView *)textView
  515. {
  516. [self _willShowInputTextViewToHeight:[self _getTextViewContentH:textView]];
  517. }
  518. #pragma mark - DXFaceDelegate
  519. - (void)selectedFacialView:(NSString *)str isDelete:(BOOL)isDelete
  520. {
  521. NSString *chatText = self.inputTextView.text;
  522. NSMutableAttributedString *attr = [[NSMutableAttributedString alloc] initWithAttributedString:self.inputTextView.attributedText];
  523. if (!isDelete && str.length > 0) {
  524. if (self.version >= 7.0) {
  525. NSRange range = [self.inputTextView selectedRange];
  526. [attr insertAttributedString:[[EaseEmotionEscape sharedInstance] attStringFromTextForInputView:str textFont:self.inputTextView.font] atIndex:range.location];
  527. self.inputTextView.attributedText = attr;
  528. } else {
  529. self.inputTextView.text = @"";
  530. self.inputTextView.text = [NSString stringWithFormat:@"%@%@",chatText,str];
  531. }
  532. }
  533. else {
  534. if (self.version >= 7.0) {
  535. if (chatText.length > 0) {
  536. NSInteger length = 1;
  537. if (chatText.length >= 2) {
  538. NSString *subStr = [chatText substringFromIndex:chatText.length-2];
  539. if ([EaseEmoji stringContainsEmoji:subStr]) {
  540. length = 2;
  541. }
  542. }
  543. self.inputTextView.attributedText = [self backspaceText:attr length:length];
  544. }
  545. } else {
  546. if (chatText.length >= 2)
  547. {
  548. NSString *subStr = [chatText substringFromIndex:chatText.length-2];
  549. if ([(EaseFaceView *)self.faceView stringIsFace:subStr]) {
  550. self.inputTextView.text = [chatText substringToIndex:chatText.length-2];
  551. [self textViewDidChange:self.inputTextView];
  552. return;
  553. }
  554. }
  555. if (chatText.length > 0) {
  556. self.inputTextView.text = [chatText substringToIndex:chatText.length-1];
  557. }
  558. }
  559. }
  560. [self textViewDidChange:self.inputTextView];
  561. }
  562. /*!
  563. @method
  564. @brief 删除文本光标前长度为length的字符串
  565. @discussion
  566. @param attr 待修改的富文本
  567. @param length 字符串长度
  568. @result 修改后的富文本
  569. */
  570. -(NSMutableAttributedString*)backspaceText:(NSMutableAttributedString*) attr length:(NSInteger)length
  571. {
  572. NSRange range = [self.inputTextView selectedRange];
  573. if (range.location == 0) {
  574. return attr;
  575. }
  576. [attr deleteCharactersInRange:NSMakeRange(range.location - length, length)];
  577. return attr;
  578. }
  579. - (void)sendFace
  580. {
  581. NSString *chatText = self.inputTextView.text;
  582. if (chatText.length > 0) {
  583. if ([self.delegate respondsToSelector:@selector(didSendText:)]) {
  584. if (![_inputTextView.text isEqualToString:@""]) {
  585. //转义回来
  586. NSMutableString *attStr = [[NSMutableString alloc] initWithString:self.inputTextView.attributedText.string];
  587. [_inputTextView.attributedText enumerateAttribute:NSAttachmentAttributeName
  588. inRange:NSMakeRange(0, self.inputTextView.attributedText.length)
  589. options:NSAttributedStringEnumerationReverse
  590. usingBlock:^(id value, NSRange range, BOOL *stop)
  591. {
  592. if (value) {
  593. EMTextAttachment* attachment = (EMTextAttachment*)value;
  594. NSString *str = [NSString stringWithFormat:@"%@",attachment.imageName];
  595. [attStr replaceCharactersInRange:range withString:str];
  596. }
  597. }];
  598. [self.delegate didSendText:attStr];
  599. self.inputTextView.text = @"";
  600. [self _willShowInputTextViewToHeight:[self _getTextViewContentH:self.inputTextView]];;
  601. }
  602. }
  603. }
  604. }
  605. - (void)sendFaceWithEmotion:(EaseEmotion *)emotion
  606. {
  607. if (emotion) {
  608. if ([self.delegate respondsToSelector:@selector(didSendText:withExt:)]) {
  609. [self.delegate didSendText:emotion.emotionTitle withExt:@{EASEUI_EMOTION_DEFAULT_EXT:emotion}];
  610. [self _willShowInputTextViewToHeight:[self _getTextViewContentH:self.inputTextView]];;
  611. }
  612. }
  613. }
  614. #pragma mark - UIKeyboardNotification
  615. - (void)chatKeyboardWillChangeFrame:(NSNotification *)notification
  616. {
  617. NSDictionary *userInfo = notification.userInfo;
  618. CGRect endFrame = [userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
  619. CGRect beginFrame = [userInfo[UIKeyboardFrameBeginUserInfoKey] CGRectValue];
  620. CGFloat duration = [userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue];
  621. UIViewAnimationCurve curve = [userInfo[UIKeyboardAnimationCurveUserInfoKey] integerValue];
  622. void(^animations)() = ^{
  623. [self _willShowKeyboardFromFrame:beginFrame toFrame:endFrame];
  624. };
  625. [UIView animateWithDuration:duration delay:0.0f options:(curve << 16 | UIViewAnimationOptionBeginFromCurrentState) animations:animations completion:nil];
  626. }
  627. #pragma mark - action
  628. - (void)styleButtonAction:(id)sender
  629. {
  630. UIButton *button = (UIButton *)sender;
  631. button.selected = !button.selected;
  632. if (button.selected) {
  633. for (EaseChatToolbarItem *item in self.rightItems) {
  634. item.button.selected = NO;
  635. }
  636. for (EaseChatToolbarItem *item in self.leftItems) {
  637. if (item.button != button) {
  638. item.button.selected = NO;
  639. }
  640. }
  641. [self _willShowBottomView:nil];
  642. self.inputTextView.text = @"";
  643. [self textViewDidChange:self.inputTextView];
  644. [self.inputTextView resignFirstResponder];
  645. }
  646. else{
  647. [self.inputTextView becomeFirstResponder];
  648. }
  649. [UIView animateWithDuration:0.2 delay:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
  650. self.recordButton.hidden = !button.selected;
  651. self.inputTextView.hidden = button.selected;
  652. } completion:nil];
  653. }
  654. - (void)faceButtonAction:(id)sender
  655. {
  656. UIButton *button = (UIButton *)sender;
  657. button.selected = !button.selected;
  658. EaseChatToolbarItem *faceItem = nil;
  659. for (EaseChatToolbarItem *item in self.rightItems) {
  660. if (item.button == button){
  661. faceItem = item;
  662. continue;
  663. }
  664. item.button.selected = NO;
  665. }
  666. for (EaseChatToolbarItem *item in self.leftItems) {
  667. item.button.selected = NO;
  668. }
  669. if (button.selected) {
  670. [self.inputTextView resignFirstResponder];
  671. [self _willShowBottomView:faceItem.button2View];
  672. [UIView animateWithDuration:0.2 delay:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
  673. self.recordButton.hidden = button.selected;
  674. self.inputTextView.hidden = !button.selected;
  675. } completion:^(BOOL finished) {
  676. }];
  677. } else {
  678. [self.inputTextView becomeFirstResponder];
  679. }
  680. }
  681. - (void)moreButtonAction:(id)sender
  682. {
  683. UIButton *button = (UIButton *)sender;
  684. button.selected = !button.selected;
  685. EaseChatToolbarItem *moreItem = nil;
  686. for (EaseChatToolbarItem *item in self.rightItems) {
  687. if (item.button == button){
  688. moreItem = item;
  689. continue;
  690. }
  691. item.button.selected = NO;
  692. }
  693. for (EaseChatToolbarItem *item in self.leftItems) {
  694. item.button.selected = NO;
  695. }
  696. if (button.selected) {
  697. [self.inputTextView resignFirstResponder];
  698. [self _willShowBottomView:moreItem.button2View];
  699. [UIView animateWithDuration:0.2 delay:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
  700. self.recordButton.hidden = button.selected;
  701. self.inputTextView.hidden = !button.selected;
  702. } completion:nil];
  703. }
  704. else
  705. {
  706. [self.inputTextView becomeFirstResponder];
  707. }
  708. }
  709. - (void)recordButtonTouchDown
  710. {
  711. if (_delegate && [_delegate respondsToSelector:@selector(didStartRecordingVoiceAction:)]) {
  712. [_delegate didStartRecordingVoiceAction:self.recordView];
  713. }
  714. }
  715. - (void)recordButtonTouchUpOutside
  716. {
  717. if (_delegate && [_delegate respondsToSelector:@selector(didCancelRecordingVoiceAction:)])
  718. {
  719. [_delegate didCancelRecordingVoiceAction:self.recordView];
  720. }
  721. }
  722. - (void)recordButtonTouchUpInside
  723. {
  724. self.recordButton.enabled = NO;
  725. if ([self.delegate respondsToSelector:@selector(didFinishRecoingVoiceAction:)])
  726. {
  727. [self.delegate didFinishRecoingVoiceAction:self.recordView];
  728. }
  729. self.recordButton.enabled = YES;
  730. }
  731. - (void)recordDragOutside
  732. {
  733. if ([self.delegate respondsToSelector:@selector(didDragOutsideAction:)])
  734. {
  735. [self.delegate didDragOutsideAction:self.recordView];
  736. }
  737. }
  738. - (void)recordDragInside
  739. {
  740. if ([self.delegate respondsToSelector:@selector(didDragInsideAction:)])
  741. {
  742. [self.delegate didDragInsideAction:self.recordView];
  743. }
  744. }
  745. #pragma mark - public
  746. + (CGFloat)defaultHeight
  747. {
  748. return 5 * 2 + 36;
  749. }
  750. - (BOOL)endEditing:(BOOL)force
  751. {
  752. BOOL result = [super endEditing:force];
  753. for (EaseChatToolbarItem *item in self.rightItems) {
  754. item.button.selected = NO;
  755. }
  756. [self _willShowBottomView:nil];
  757. return result;
  758. }
  759. - (void)cancelTouchRecord
  760. {
  761. if ([_recordView isKindOfClass:[EaseRecordView class]]) {
  762. [(EaseRecordView *)_recordView recordButtonTouchUpInside];
  763. [_recordView removeFromSuperview];
  764. }
  765. }
  766. - (void)willShowBottomView:(UIView *)bottomView
  767. {
  768. [self _willShowBottomView:bottomView];
  769. }
  770. @end