TUIInputController.m 30 KB

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