TUIInputController_Minimalist.m 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627
  1. //
  2. // TInputController.m
  3. // UIKit
  4. //
  5. // Created by kennethmiao on 2018/9/18.
  6. // Copyright © 2018 Tencent. All rights reserved.
  7. //
  8. #import "TUIInputController_Minimalist.h"
  9. #import <AVFoundation/AVFoundation.h>
  10. #import <TIMCommon/NSString+TUIEmoji.h>
  11. #import <TIMCommon/TIMCommonModel.h>
  12. #import <TIMCommon/TIMDefine.h>
  13. #import <TUICore/TUIDarkModel.h>
  14. #import <TUICore/TUIThemeManager.h>
  15. #import "TUIChatConfig.h"
  16. #import "TUIChatDataProvider.h"
  17. #import "TUIChatModifyMessageHelper.h"
  18. #import "TUICloudCustomDataTypeCenter.h"
  19. #import "TUIFaceMessageCell_Minimalist.h"
  20. #import "TUIMenuCellData.h"
  21. #import "TUIMenuCell_Minimalist.h"
  22. #import "TUIMessageDataProvider.h"
  23. #import "TUITextMessageCell_Minimalist.h"
  24. #import "TUIVoiceMessageCell_Minimalist.h"
  25. #import <TIMCommon/TIMCommonMediator.h>
  26. #import <TIMCommon/TUIEmojiMeditorProtocol.h>
  27. @interface TUIInputController_Minimalist () <TUIInputBarDelegate_Minimalist, TUIMenuViewDelegate_Minimalist, TUIFaceViewDelegate>
  28. @property(nonatomic, assign) InputStatus status;
  29. @property(nonatomic, assign) CGRect keyboardFrame;
  30. @property(nonatomic, copy) void (^modifyRootReplyMsgBlock)(TUIMessageCellData *);
  31. @end
  32. @implementation TUIInputController_Minimalist
  33. - (void)viewDidLoad {
  34. [super viewDidLoad];
  35. [self setupViews];
  36. }
  37. - (void)viewWillAppear:(BOOL)animated {
  38. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
  39. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
  40. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillChangeFrame:) name:UIKeyboardWillChangeFrameNotification object:nil];
  41. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(inputMessageStatusChanged:) name:@"kTUINotifyMessageStatusChanged" object:nil];
  42. }
  43. - (void)viewDidAppear:(BOOL)animated {
  44. [super viewDidAppear:animated];
  45. for (UIGestureRecognizer *gesture in self.view.window.gestureRecognizers) {
  46. NSLog(@"gesture = %@", gesture);
  47. gesture.delaysTouchesBegan = NO;
  48. NSLog(@"delaysTouchesBegan = %@", gesture.delaysTouchesBegan ? @"YES" : @"NO");
  49. NSLog(@"delaysTouchesEnded = %@", gesture.delaysTouchesEnded ? @"YES" : @"NO");
  50. }
  51. self.navigationController.interactivePopGestureRecognizer.delaysTouchesBegan = NO;
  52. }
  53. - (void)viewDidDisappear:(BOOL)animated {
  54. [[NSNotificationCenter defaultCenter] removeObserver:self];
  55. }
  56. - (void)setupViews {
  57. self.view.backgroundColor = RGBA(255, 255, 255, 1);
  58. _status = Input_Status_Input;
  59. _inputBar =
  60. [[TUIInputBar_Minimalist alloc] initWithFrame:CGRectMake(0, CGRectGetMaxY(self.replyPreviewBar.frame), self.view.frame.size.width, TTextView_Height)];
  61. _inputBar.delegate = self;
  62. [self.view addSubview:_inputBar];
  63. }
  64. - (void)keyboardWillHide:(NSNotification *)notification {
  65. if (_delegate && [_delegate respondsToSelector:@selector(inputController:didChangeHeight:)]) {
  66. CGFloat inputContainerBottom = [self getInputContainerBottom];
  67. [_delegate inputController:self didChangeHeight:inputContainerBottom + Bottom_SafeHeight];
  68. }
  69. if (_status == Input_Status_Input_Keyboard) {
  70. _status = Input_Status_Input;
  71. }
  72. }
  73. - (void)keyboardWillShow:(NSNotification *)notification {
  74. if (_status == Input_Status_Input_Face) {
  75. [self hideFaceAnimation];
  76. } else {
  77. //[self hideFaceAnimation:NO];
  78. //[self hideMoreAnimation:NO];
  79. }
  80. _status = Input_Status_Input_Keyboard;
  81. }
  82. - (void)keyboardWillChangeFrame:(NSNotification *)notification {
  83. CGRect keyboardFrame = [notification.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
  84. if (_delegate && [_delegate respondsToSelector:@selector(inputController:didChangeHeight:)]) {
  85. CGFloat inputContainerBottom = [self getInputContainerBottom];
  86. [_delegate inputController:self didChangeHeight:keyboardFrame.size.height + inputContainerBottom];
  87. }
  88. self.keyboardFrame = keyboardFrame;
  89. }
  90. - (void)hideFaceAnimation {
  91. self.faceSegementScrollView.hidden = NO;
  92. self.faceSegementScrollView.alpha = 1.0;
  93. self.menuView.hidden = NO;
  94. self.menuView.alpha = 1.0;
  95. __weak typeof(self) ws = self;
  96. [UIView animateWithDuration:0.3
  97. delay:0
  98. options:UIViewAnimationOptionCurveEaseOut
  99. animations:^{
  100. ws.faceSegementScrollView.alpha = 0.0;
  101. ws.menuView.alpha = 0.0;
  102. }
  103. completion:^(BOOL finished) {
  104. ws.faceSegementScrollView.hidden = YES;
  105. ws.faceSegementScrollView.alpha = 1.0;
  106. ws.menuView.hidden = YES;
  107. ws.menuView.alpha = 1.0;
  108. [ws.menuView removeFromSuperview];
  109. [ws.faceSegementScrollView removeFromSuperview];
  110. ws.view.backgroundColor = RGBA(255, 255, 255, 1);
  111. }];
  112. }
  113. - (void)showFaceAnimation {
  114. [self.view addSubview:self.faceSegementScrollView];
  115. [self.view addSubview:self.menuView];
  116. __weak typeof(self) ws = self;
  117. [self.faceSegementScrollView updateRecentView];
  118. [self.faceSegementScrollView setAllFloatCtrlViewAllowSendSwitch:(self.inputBar.inputTextView.text.length > 0)?YES:NO];
  119. self.faceSegementScrollView.onScrollCallback = ^(NSInteger indexPage) {
  120. [ws.menuView scrollToMenuIndex:indexPage];
  121. };
  122. self.inputBar.inputBarTextChanged = ^(UITextView *textview) {
  123. if(textview.text.length > 0) {
  124. [ws.faceSegementScrollView setAllFloatCtrlViewAllowSendSwitch:YES];
  125. }
  126. else {
  127. [ws.faceSegementScrollView setAllFloatCtrlViewAllowSendSwitch:NO];
  128. }
  129. };
  130. self.faceSegementScrollView.hidden = NO;
  131. CGRect frame = self.menuView.frame;
  132. frame.origin.y = self.view.window.frame.size.height;
  133. self.menuView.frame = frame;
  134. self.menuView.hidden = NO;
  135. frame = self.faceSegementScrollView.frame;
  136. frame.origin.y = self.menuView.frame.origin.y + self.menuView.frame.size.height;
  137. self.faceSegementScrollView.frame = frame;
  138. [UIView animateWithDuration:0.3
  139. delay:0
  140. options:UIViewAnimationOptionCurveEaseOut
  141. animations:^{
  142. CGRect newFrame = ws.menuView.frame;
  143. newFrame.origin.y = CGRectGetMaxY(ws.inputBar.frame); // ws.inputBar.frame.origin.y + ws.inputBar.frame.size.height;
  144. ws.menuView.frame = newFrame;
  145. newFrame = ws.faceSegementScrollView.frame;
  146. newFrame.origin.y = ws.menuView.frame.origin.y + ws.menuView.frame.size.height;
  147. ws.faceSegementScrollView.frame = newFrame;
  148. ws.view.backgroundColor = TUIChatDynamicColor(@"chat_input_controller_bg_color", @"#EBF0F6");
  149. }
  150. completion:nil];
  151. }
  152. - (void)inputBarDidTouchCamera:(TUIInputBar_Minimalist *)textView {
  153. [_inputBar.inputTextView resignFirstResponder];
  154. [self hideFaceAnimation];
  155. _status = Input_Status_Input_Camera;
  156. if (_delegate && [_delegate respondsToSelector:@selector(inputController:didChangeHeight:)]) {
  157. CGFloat inputContainerBottom = [self getInputContainerBottom];
  158. [_delegate inputController:self didChangeHeight:inputContainerBottom + Bottom_SafeHeight];
  159. }
  160. if (_delegate && [_delegate respondsToSelector:@selector(inputControllerDidSelectCamera:)]) {
  161. [_delegate inputControllerDidSelectCamera:self];
  162. }
  163. }
  164. - (void)inputBarDidTouchMore:(TUIInputBar_Minimalist *)textView {
  165. if (self.delegate && [self.delegate respondsToSelector:@selector(inputControllerDidSelectMoreButton:)]) {
  166. [self.delegate inputControllerDidSelectMoreButton:self];
  167. }
  168. }
  169. - (void)inputBarDidTouchFace:(TUIInputBar_Minimalist *)textView {
  170. if ([TIMConfig defaultConfig].faceGroups.count == 0) {
  171. return;
  172. }
  173. [_inputBar.inputTextView resignFirstResponder];
  174. _status = Input_Status_Input_Face;
  175. if (_delegate && [_delegate respondsToSelector:@selector(inputController:didChangeHeight:)]) {
  176. [_delegate inputController:self
  177. didChangeHeight:CGRectGetMaxY(_inputBar.frame) + self.faceSegementScrollView.frame.size.height + self.menuView.frame.size.height ];
  178. }
  179. [self showFaceAnimation];
  180. }
  181. - (void)inputBarDidTouchKeyboard:(TUIInputBar_Minimalist *)textView {
  182. if (_status == Input_Status_Input_Face) {
  183. [self hideFaceAnimation];
  184. }
  185. _status = Input_Status_Input_Keyboard;
  186. [_inputBar.inputTextView becomeFirstResponder];
  187. }
  188. - (void)inputBar:(TUIInputBar_Minimalist *)textView didChangeInputHeight:(CGFloat)offset {
  189. if (_status == Input_Status_Input_Face) {
  190. [self showFaceAnimation];
  191. }
  192. if (_delegate && [_delegate respondsToSelector:@selector(inputController:didChangeHeight:)]) {
  193. [_delegate inputController:self didChangeHeight:self.view.frame.size.height + offset];
  194. if (_referencePreviewBar) {
  195. CGRect referencePreviewBarFrame = _referencePreviewBar.frame;
  196. _referencePreviewBar.frame = CGRectMake(referencePreviewBarFrame.origin.x, referencePreviewBarFrame.origin.y + offset,
  197. referencePreviewBarFrame.size.width, referencePreviewBarFrame.size.height);
  198. }
  199. }
  200. }
  201. - (void)inputBar:(TUIInputBar_Minimalist *)textView didSendText:(NSString *)text {
  202. /**
  203. * Emoticon internationalization --> restore to actual Chinese key
  204. */
  205. NSString *content = [text getInternationalStringWithfaceContent];
  206. V2TIMMessage *message = [[V2TIMManager sharedInstance] createTextMessage:content];
  207. [self appendReplyDataIfNeeded:message];
  208. [self appendReferenceDataIfNeeded:message];
  209. if (_delegate && [_delegate respondsToSelector:@selector(inputController:didSendMessage:)]) {
  210. [_delegate inputController:self didSendMessage:message];
  211. }
  212. }
  213. - (void)inputMessageStatusChanged:(NSNotification *)noti {
  214. NSDictionary *userInfo = noti.userInfo;
  215. TUIMessageCellData *msg = userInfo[@"msg"];
  216. long status = [userInfo[@"status"] intValue];
  217. if ([msg isKindOfClass:TUIMessageCellData.class] && status == Msg_Status_Succ) {
  218. dispatch_async(dispatch_get_main_queue(), ^{
  219. if (self.modifyRootReplyMsgBlock) {
  220. self.modifyRootReplyMsgBlock(msg);
  221. }
  222. });
  223. }
  224. }
  225. - (void)appendReplyDataIfNeeded:(V2TIMMessage *)message {
  226. if (self.replyData) {
  227. V2TIMMessage *parentMsg = self.replyData.originMessage;
  228. NSMutableDictionary *simpleReply = [NSMutableDictionary dictionary];
  229. [simpleReply addEntriesFromDictionary:@{
  230. @"messageID" : self.replyData.msgID ?: @"",
  231. @"messageAbstract" : [self.replyData.msgAbstract ?: @"" getInternationalStringWithfaceContent],
  232. @"messageSender" : self.replyData.sender ?: @"",
  233. @"messageType" : @(self.replyData.type),
  234. @"messageTime" : @(self.replyData.originMessage.timestamp ? [self.replyData.originMessage.timestamp timeIntervalSince1970] : 0),
  235. @"messageSequence" : @(self.replyData.originMessage.seq),
  236. @"version" : @(kMessageReplyVersion),
  237. }];
  238. NSMutableDictionary *cloudResultDic = [[NSMutableDictionary alloc] initWithCapacity:5];
  239. if (parentMsg.cloudCustomData) {
  240. NSDictionary *originDic = [TUITool jsonData2Dictionary:parentMsg.cloudCustomData];
  241. if (originDic && [originDic isKindOfClass:[NSDictionary class]]) {
  242. [cloudResultDic addEntriesFromDictionary:originDic];
  243. }
  244. /**
  245. * Accept the data in the parent, but cannot save messageReplies\messageReact, because the root message topic creator has this field.
  246. * messageReplies\messageReact cannot be stored in the new message currently sent
  247. */
  248. [cloudResultDic removeObjectForKey:@"messageReplies"];
  249. [cloudResultDic removeObjectForKey:@"messageReact"];
  250. }
  251. NSString *messageParentReply = cloudResultDic[@"messageReply"];
  252. NSString *messageRootID = [messageParentReply valueForKey:@"messageRootID"];
  253. if (self.replyData.messageRootID.length > 0) {
  254. messageRootID = self.replyData.messageRootID;
  255. }
  256. if (!IS_NOT_EMPTY_NSSTRING(messageRootID)) {
  257. /**
  258. * If the original message does not have a messageRootID, you need to use the msgID of the current original message as root
  259. */
  260. if (IS_NOT_EMPTY_NSSTRING(parentMsg.msgID)) {
  261. messageRootID = parentMsg.msgID;
  262. }
  263. }
  264. [simpleReply setObject:messageRootID forKey:@"messageRootID"];
  265. [cloudResultDic setObject:simpleReply forKey:@"messageReply"];
  266. NSData *data = [TUITool dictionary2JsonData:cloudResultDic];
  267. if (data) {
  268. message.cloudCustomData = data;
  269. }
  270. [self exitReplyAndReference:nil];
  271. __weak typeof(self) weakSelf = self;
  272. self.modifyRootReplyMsgBlock = ^(TUIMessageCellData *cellData) {
  273. __strong typeof(self) strongSelf = weakSelf;
  274. [strongSelf modifyRootReplyMsgByID:messageRootID currentMsg:cellData];
  275. strongSelf.modifyRootReplyMsgBlock = nil;
  276. };
  277. }
  278. }
  279. - (void)modifyRootReplyMsgByID:(NSString *)messageRootID currentMsg:(TUIMessageCellData *)messageCellData {
  280. NSDictionary *simpleCurrentContent = @{
  281. @"messageID" : messageCellData.innerMessage.msgID ?: @"",
  282. @"messageAbstract" : [messageCellData.innerMessage.textElem.text ?: @"" getInternationalStringWithfaceContent],
  283. @"messageSender" : messageCellData.innerMessage.sender ?: @"",
  284. @"messageType" : @(messageCellData.innerMessage.elemType),
  285. @"messageTime" : @(messageCellData.innerMessage.timestamp ? [messageCellData.innerMessage.timestamp timeIntervalSince1970] : 0),
  286. @"messageSequence" : @(messageCellData.innerMessage.seq),
  287. @"version" : @(kMessageReplyVersion)
  288. };
  289. if (messageRootID) {
  290. [TUIChatDataProvider findMessages:@[ messageRootID ]
  291. callback:^(BOOL succ, NSString *_Nonnull error_message, NSArray *_Nonnull msgs) {
  292. if (succ) {
  293. if (msgs.count > 0) {
  294. V2TIMMessage *rootMsg = msgs.firstObject;
  295. [[TUIChatModifyMessageHelper defaultHelper] modifyMessage:rootMsg
  296. simpleCurrentContent:simpleCurrentContent];
  297. }
  298. }
  299. }];
  300. }
  301. }
  302. - (void)appendReferenceDataIfNeeded:(V2TIMMessage *)message {
  303. if (self.referenceData) {
  304. NSDictionary *dict = @{
  305. @"messageReply" : @{
  306. @"messageID" : self.referenceData.msgID ?: @"",
  307. @"messageAbstract" : [self.referenceData.msgAbstract ?: @"" getInternationalStringWithfaceContent],
  308. @"messageSender" : self.referenceData.sender ?: @"",
  309. @"messageType" : @(self.referenceData.type),
  310. @"messageTime" : @(self.referenceData.originMessage.timestamp ? [self.referenceData.originMessage.timestamp timeIntervalSince1970] : 0),
  311. @"messageSequence" : @(self.referenceData.originMessage.seq),
  312. @"version" : @(kMessageReplyVersion)
  313. }
  314. };
  315. NSError *error = nil;
  316. NSData *data = [NSJSONSerialization dataWithJSONObject:dict options:0 error:&error];
  317. if (error == nil) {
  318. message.cloudCustomData = data;
  319. }
  320. [self exitReplyAndReference:nil];
  321. }
  322. }
  323. - (void)inputBar:(TUIInputBar_Minimalist *)textView didSendVoice:(NSString *)path {
  324. NSURL *url = [NSURL fileURLWithPath:path];
  325. AVURLAsset *audioAsset = [AVURLAsset URLAssetWithURL:url options:nil];
  326. float duration = (float)CMTimeGetSeconds(audioAsset.duration);
  327. int formatDuration = duration > 59 ? 60 : duration + 1 ;
  328. V2TIMMessage *message = [[V2TIMManager sharedInstance] createSoundMessage:path duration:formatDuration];
  329. if (message && _delegate && [_delegate respondsToSelector:@selector(inputController:didSendMessage:)]) {
  330. [_delegate inputController:self didSendMessage:message];
  331. }
  332. }
  333. - (void)inputBarDidInputAt:(TUIInputBar_Minimalist *)textView {
  334. if (_delegate && [_delegate respondsToSelector:@selector(inputControllerDidInputAt:)]) {
  335. [_delegate inputControllerDidInputAt:self];
  336. }
  337. }
  338. - (void)inputBar:(TUIInputBar_Minimalist *)textView didDeleteAt:(NSString *)atText {
  339. if (_delegate && [_delegate respondsToSelector:@selector(inputController:didDeleteAt:)]) {
  340. [_delegate inputController:self didDeleteAt:atText];
  341. }
  342. }
  343. - (void)inputBarDidDeleteBackward:(TUIInputBar_Minimalist *)textView {
  344. if (textView.inputTextView.text.length == 0) {
  345. [self exitReplyAndReference:nil];
  346. }
  347. }
  348. - (void)inputTextViewShouldBeginTyping:(UITextView *)textView {
  349. if (_delegate && [_delegate respondsToSelector:@selector(inputControllerBeginTyping:)]) {
  350. [_delegate inputControllerBeginTyping:self];
  351. }
  352. }
  353. - (void)inputTextViewShouldEndTyping:(UITextView *)textView {
  354. if (_delegate && [_delegate respondsToSelector:@selector(inputControllerEndTyping:)]) {
  355. [_delegate inputControllerEndTyping:self];
  356. }
  357. }
  358. - (void)reset {
  359. if (_status == Input_Status_Input) {
  360. return;
  361. } else if (_status == Input_Status_Input_Face) {
  362. [self hideFaceAnimation];
  363. }
  364. _status = Input_Status_Input;
  365. [_inputBar.inputTextView resignFirstResponder];
  366. if (_delegate && [_delegate respondsToSelector:@selector(inputController:didChangeHeight:)]) {
  367. CGFloat inputContainerBottom = [self getInputContainerBottom];
  368. [_delegate inputController:self didChangeHeight:inputContainerBottom + Bottom_SafeHeight];
  369. }
  370. }
  371. - (void)showReferencePreview:(TUIReferencePreviewData *)data {
  372. self.referenceData = data;
  373. [self.referencePreviewBar removeFromSuperview];
  374. [self.view addSubview:self.referencePreviewBar];
  375. self.inputBar.lineView.hidden = YES;
  376. self.referencePreviewBar.previewReferenceData = data;
  377. self.inputBar.mm_y = 0;
  378. self.referencePreviewBar.frame = CGRectMake(0, 0, self.view.bounds.size.width, TMenuView_Menu_Height);
  379. self.referencePreviewBar.mm_y = CGRectGetMaxY(self.inputBar.frame);
  380. // Set the default position to solve the UI confusion when the keyboard does not become the first responder
  381. if (self.delegate && [self.delegate respondsToSelector:@selector(inputController:didChangeHeight:)]) {
  382. [self.delegate inputController:self didChangeHeight:CGRectGetMaxY(self.inputBar.frame) + Bottom_SafeHeight + TMenuView_Menu_Height];
  383. }
  384. if (self.status == Input_Status_Input_Keyboard) {
  385. CGFloat keyboradHeight = self.keyboardFrame.size.height;
  386. if (self.delegate && [self.delegate respondsToSelector:@selector(inputController:didChangeHeight:)]) {
  387. [self.delegate inputController:self didChangeHeight:CGRectGetMaxY(self.referencePreviewBar.frame) + keyboradHeight];
  388. }
  389. } else if (self.status == Input_Status_Input_Face) {
  390. [self.inputBar changeToKeyboard];
  391. } else {
  392. [self.inputBar.inputTextView becomeFirstResponder];
  393. }
  394. }
  395. - (void)showReplyPreview:(TUIReplyPreviewData *)data {
  396. self.replyData = data;
  397. [self.replyPreviewBar removeFromSuperview];
  398. [self.view addSubview:self.replyPreviewBar];
  399. self.inputBar.lineView.hidden = YES;
  400. self.replyPreviewBar.previewData = data;
  401. self.replyPreviewBar.frame = CGRectMake(0, 0, self.view.bounds.size.width, TMenuView_Menu_Height);
  402. self.inputBar.mm_y = CGRectGetMaxY(self.replyPreviewBar.frame);
  403. // Set the default position to solve the UI confusion when the keyboard does not become the first responder
  404. if (self.delegate && [self.delegate respondsToSelector:@selector(inputController:didChangeHeight:)]) {
  405. [self.delegate inputController:self didChangeHeight:CGRectGetMaxY(self.inputBar.frame) + Bottom_SafeHeight];
  406. }
  407. if (self.status == Input_Status_Input_Keyboard) {
  408. CGFloat keyboradHeight = self.keyboardFrame.size.height;
  409. if (self.delegate && [self.delegate respondsToSelector:@selector(inputController:didChangeHeight:)]) {
  410. [self.delegate inputController:self didChangeHeight:CGRectGetMaxY(self.inputBar.frame) + keyboradHeight];
  411. }
  412. } else if (self.status == Input_Status_Input_Face) {
  413. [self.inputBar changeToKeyboard];
  414. } else {
  415. [self.inputBar.inputTextView becomeFirstResponder];
  416. }
  417. }
  418. - (void)exitReplyAndReference:(void (^__nullable)(void))finishedCallback {
  419. if (self.replyData == nil && self.referenceData == nil) {
  420. if (finishedCallback) {
  421. finishedCallback();
  422. }
  423. return;
  424. }
  425. self.replyData = nil;
  426. self.referenceData = nil;
  427. __weak typeof(self) weakSelf = self;
  428. [UIView animateWithDuration:0.25
  429. animations:^{
  430. weakSelf.replyPreviewBar.hidden = YES;
  431. weakSelf.referencePreviewBar.hidden = YES;
  432. weakSelf.inputBar.mm_y = 0;
  433. if (weakSelf.status == Input_Status_Input_Keyboard) {
  434. CGFloat keyboradHeight = weakSelf.keyboardFrame.size.height;
  435. if (weakSelf.delegate && [weakSelf.delegate respondsToSelector:@selector(inputController:didChangeHeight:)]) {
  436. [weakSelf.delegate inputController:weakSelf didChangeHeight:CGRectGetMaxY(weakSelf.inputBar.frame) + keyboradHeight];
  437. }
  438. } else {
  439. if (weakSelf.delegate && [weakSelf.delegate respondsToSelector:@selector(inputController:didChangeHeight:)]) {
  440. [weakSelf.delegate inputController:weakSelf didChangeHeight:CGRectGetMaxY(weakSelf.inputBar.frame) + Bottom_SafeHeight];
  441. }
  442. }
  443. }
  444. completion:^(BOOL finished) {
  445. [weakSelf.replyPreviewBar removeFromSuperview];
  446. [weakSelf.referencePreviewBar removeFromSuperview];
  447. weakSelf.replyPreviewBar = nil;
  448. weakSelf.referencePreviewBar = nil;
  449. [weakSelf hideFaceAnimation];
  450. weakSelf.inputBar.lineView.hidden = NO;
  451. if (finishedCallback) {
  452. finishedCallback();
  453. }
  454. }];
  455. }
  456. - (void)menuView:(TUIMenuView_Minimalist *)menuView didSelectItemAtIndex:(NSInteger)index {
  457. [self.faceSegementScrollView setPageIndex:index];
  458. }
  459. - (void)menuViewDidSendMessage:(TUIMenuView_Minimalist *)menuView {
  460. NSString *text = [_inputBar getInput];
  461. if ([text isEqualToString:@""]) {
  462. return;
  463. }
  464. /**
  465. * Emoticon internationalization --> restore to actual Chinese key
  466. */
  467. NSString *content = [text getInternationalStringWithfaceContent];
  468. [_inputBar clearInput];
  469. V2TIMMessage *message = [[V2TIMManager sharedInstance] createTextMessage:content];
  470. [self appendReplyDataIfNeeded:message];
  471. [self appendReferenceDataIfNeeded:message];
  472. if (_delegate && [_delegate respondsToSelector:@selector(inputController:didSendMessage:)]) {
  473. [_delegate inputController:self didSendMessage:message];
  474. }
  475. }
  476. - (void)faceView:(TUIFaceView *)faceView scrollToFaceGroupIndex:(NSInteger)index {
  477. [self.menuView scrollToMenuIndex:index];
  478. }
  479. - (void)faceViewDidBackDelete:(TUIFaceView *)faceView {
  480. [_inputBar backDelete];
  481. }
  482. - (void)faceViewClickSendMessageBtn {
  483. [self menuViewDidSendMessage:self.menuView];
  484. }
  485. - (void)faceView:(TUIFaceView *)faceView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
  486. TUIFaceGroup *group = faceView.faceGroups[indexPath.section];
  487. TUIFaceCellData *face = group.faces[indexPath.row];
  488. if (group.isNeedAddInInputBar) {
  489. [_inputBar addEmoji:face];
  490. [self updateRecentMenuQueue:face.name];
  491. } else {
  492. if (face.name) {
  493. V2TIMMessage *message = [[V2TIMManager sharedInstance] createFaceMessage:group.groupIndex data:[face.name dataUsingEncoding:NSUTF8StringEncoding]];
  494. if (_delegate && [_delegate respondsToSelector:@selector(inputController:didSendMessage:)]) {
  495. [_delegate inputController:self didSendMessage:message];
  496. }
  497. }
  498. }
  499. }
  500. - (void)updateRecentMenuQueue:(NSString *)faceName {
  501. id<TUIEmojiMeditorProtocol> service = [[TIMCommonMediator share] getObject:@protocol(TUIEmojiMeditorProtocol)];
  502. return [service updateRecentMenuQueue:faceName];
  503. }
  504. #pragma mark - lazy load
  505. - (TUIFaceSegementScrollView *)faceSegementScrollView {
  506. if(!_faceSegementScrollView) {
  507. _faceSegementScrollView = [[TUIFaceSegementScrollView alloc]
  508. initWithFrame:CGRectMake(0,
  509. _inputBar.frame.origin.y + _inputBar.frame.size.height, self.view.frame.size.width,
  510. TFaceView_Height)];
  511. [_faceSegementScrollView setItems:[TIMConfig defaultConfig].faceGroups delegate:self];
  512. }
  513. return _faceSegementScrollView;
  514. }
  515. - (TUIMenuView_Minimalist *)menuView {
  516. if (!_menuView) {
  517. _menuView = [[TUIMenuView_Minimalist alloc] initWithFrame:CGRectMake(16, _inputBar.mm_maxY, self.view.frame.size.width - 32, TMenuView_Menu_Height)];
  518. _menuView.delegate = self;
  519. TIMConfig *config = [TIMConfig defaultConfig];
  520. NSMutableArray *menus = [NSMutableArray array];
  521. for (NSInteger i = 0; i < config.faceGroups.count; ++i) {
  522. TUIFaceGroup *group = config.faceGroups[i];
  523. TUIMenuCellData *data = [[TUIMenuCellData alloc] init];
  524. data.path = group.menuPath;
  525. data.isSelected = NO;
  526. if (i == 0) {
  527. data.isSelected = YES;
  528. }
  529. [menus addObject:data];
  530. }
  531. [_menuView setData:menus];
  532. }
  533. return _menuView;
  534. }
  535. - (TUIReplyPreviewBar_Minimalist *)replyPreviewBar {
  536. if (_replyPreviewBar == nil) {
  537. _replyPreviewBar = [[TUIReplyPreviewBar_Minimalist alloc] init];
  538. __weak typeof(self) weakSelf = self;
  539. _replyPreviewBar.onClose = ^{
  540. __strong typeof(weakSelf) strongSelf = weakSelf;
  541. [strongSelf exitReplyAndReference:nil];
  542. };
  543. }
  544. return _replyPreviewBar;
  545. }
  546. - (TUIReferencePreviewBar_Minimalist *)referencePreviewBar {
  547. if (_referencePreviewBar == nil) {
  548. _referencePreviewBar = [[TUIReferencePreviewBar_Minimalist alloc] init];
  549. __weak typeof(self) weakSelf = self;
  550. _referencePreviewBar.onClose = ^{
  551. __strong typeof(weakSelf) strongSelf = weakSelf;
  552. [strongSelf exitReplyAndReference:nil];
  553. };
  554. }
  555. return _referencePreviewBar;
  556. }
  557. - (CGFloat)getInputContainerBottom {
  558. CGFloat inputHeight = CGRectGetMaxY(_inputBar.frame);
  559. if (_referencePreviewBar) {
  560. inputHeight = CGRectGetMaxY(_referencePreviewBar.frame);
  561. }
  562. return inputHeight;
  563. }
  564. @end