TUIInputController.m 30 KB

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