NIMSessionLayoutImpl.m 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. //
  2. // NIMSessionLayout.m
  3. // NIMKit
  4. //
  5. // Created by chris on 2016/11/8.
  6. // Copyright © 2016年 NetEase. All rights reserved.
  7. //
  8. #import "NIMSessionLayoutImpl.h"
  9. #import "UITableView+NIMScrollToBottom.h"
  10. #import "NIMMessageCell.h"
  11. #import "NIMGlobalMacro.h"
  12. #import "NIMSessionTableAdapter.h"
  13. #import "UIView+NIM.h"
  14. #import "NIMKitKeyboardInfo.h"
  15. @interface NIMSessionLayoutImpl()
  16. {
  17. NSMutableArray *_inserts;
  18. CGFloat _inputViewHeight;
  19. }
  20. @property (nonatomic,strong) UIRefreshControl *refreshControl;
  21. @property (nonatomic,strong) NIMSession *session;
  22. @property (nonatomic,strong) id<NIMSessionConfig> sessionConfig;
  23. @property (nonatomic,weak) id<NIMSessionLayoutDelegate> delegate;
  24. @end
  25. @implementation NIMSessionLayoutImpl
  26. - (instancetype)initWithSession:(NIMSession *)session
  27. config:(id<NIMSessionConfig>)sessionConfig
  28. {
  29. self = [super init];
  30. if (self) {
  31. _sessionConfig = sessionConfig;
  32. _session = session;
  33. _inserts = [[NSMutableArray alloc] init];
  34. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(menuDidHide:) name:UIMenuControllerDidHideMenuNotification object:nil];
  35. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillChangeFrame:) name:NIMKitKeyboardWillChangeFrameNotification object:nil];
  36. }
  37. return self;
  38. }
  39. - (void)dealloc
  40. {
  41. [[NSNotificationCenter defaultCenter] removeObserver:self];
  42. }
  43. - (void)reloadTable
  44. {
  45. [self.tableView reloadData];
  46. }
  47. - (void)setTableView:(UITableView *)tableView
  48. {
  49. BOOL change = _tableView != tableView;
  50. if (change)
  51. {
  52. _tableView = tableView;
  53. [self setupRefreshControl];
  54. }
  55. }
  56. - (void)resetLayout
  57. {
  58. [self adjustInputView];
  59. [self adjustTableView];
  60. }
  61. - (void)layoutAfterRefresh
  62. {
  63. [self.refreshControl endRefreshing];
  64. [self.tableView reloadData];
  65. }
  66. - (void)adjustOffset:(NSInteger)row {
  67. if (row >= 0) {
  68. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  69. NSIndexPath *indexPath = [NSIndexPath indexPathForRow:row inSection:0];
  70. [self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionTop animated:YES];
  71. });
  72. }
  73. }
  74. - (void)changeLayout:(CGFloat)inputViewHeight
  75. {
  76. BOOL change = _inputViewHeight != inputViewHeight;
  77. if (change)
  78. {
  79. _inputViewHeight = inputViewHeight;
  80. [self adjustInputView];
  81. [self adjustTableView];
  82. }
  83. }
  84. - (void)adjustInputView
  85. {
  86. UIView *superView = self.inputView.superview;
  87. UIEdgeInsets safeAreaInsets = UIEdgeInsetsZero;
  88. if (@available(iOS 11.0, *))
  89. {
  90. safeAreaInsets = superView.safeAreaInsets;
  91. }
  92. self.inputView.nim_bottom = superView.nim_height - safeAreaInsets.bottom;
  93. }
  94. - (void)adjustTableView
  95. {
  96. //输入框是否弹起
  97. BOOL inputViewUp = NO;
  98. switch (self.inputView.status)
  99. {
  100. case NIMInputStatusText:
  101. inputViewUp = [NIMKitKeyboardInfo instance].isVisiable;
  102. break;
  103. case NIMInputStatusAudio:
  104. inputViewUp = NO;
  105. break;
  106. case NIMInputStatusMore:
  107. case NIMInputStatusEmoticon:
  108. inputViewUp = YES;
  109. default:
  110. break;
  111. }
  112. self.tableView.userInteractionEnabled = !inputViewUp;
  113. CGRect rect = self.tableView.frame;
  114. //tableview 的位置
  115. UIView *superView = self.tableView.superview;
  116. UIEdgeInsets safeAreaInsets = UIEdgeInsetsZero;
  117. if (@available(iOS 11.0, *))
  118. {
  119. safeAreaInsets = superView.safeAreaInsets;
  120. }
  121. CGFloat containerSafeHeight = self.tableView.superview.frame.size.height - safeAreaInsets.bottom;
  122. rect.size.height = containerSafeHeight - self.inputView.toolBar.nim_height;
  123. //tableview 的内容 inset
  124. UIEdgeInsets contentInsets = UIEdgeInsetsZero;
  125. CGFloat visiableHeight = 0;
  126. if (@available(iOS 11.0, *))
  127. {
  128. contentInsets = self.tableView.adjustedContentInset;
  129. }
  130. else
  131. {
  132. contentInsets = self.tableView.contentInset;
  133. }
  134. [self.tableView reloadData];
  135. //如果气泡过少,少于总高度,输入框视图需要顶到最后一个气泡的下面。
  136. visiableHeight = visiableHeight + self.tableView.contentSize.height + contentInsets.top + contentInsets.bottom;
  137. visiableHeight = MIN(visiableHeight, rect.size.height);
  138. rect.origin.y = containerSafeHeight - visiableHeight - self.inputView.nim_height;
  139. rect.origin.y = rect.origin.y > 0? 0 : rect.origin.y;
  140. BOOL tableChanged = !CGRectEqualToRect(self.tableView.frame, rect);
  141. if (tableChanged)
  142. {
  143. [self.tableView setFrame:rect];
  144. [self.tableView nim_scrollToBottom:YES];
  145. }
  146. }
  147. #pragma mark - Notification
  148. - (void)menuDidHide:(NSNotification *)notification
  149. {
  150. [UIMenuController sharedMenuController].menuItems = nil;
  151. }
  152. - (void)keyboardWillChangeFrame:(NSNotification *)notification
  153. {
  154. if (!self.tableView.window)
  155. {
  156. //如果当前视图不是顶部视图,则不需要监听
  157. return;
  158. }
  159. [self.inputView sizeToFit];
  160. }
  161. #pragma mark - Private
  162. - (void)calculateContent:(NIMMessageModel *)model{
  163. [model contentSize:self.tableView.nim_width];
  164. }
  165. - (void)setupRefreshControl
  166. {
  167. self.refreshControl = [[UIRefreshControl alloc] initWithFrame:CGRectMake(0, 0, 20, 20)];
  168. if (@available(iOS 10.0, *))
  169. {
  170. self.tableView.refreshControl = self.refreshControl;
  171. }
  172. else
  173. {
  174. [self.tableView addSubview: self.refreshControl];
  175. }
  176. [self.refreshControl addTarget:self action:@selector(headerRereshing:) forControlEvents:UIControlEventValueChanged];
  177. }
  178. - (void)headerRereshing:(id)sender
  179. {
  180. if ([self.delegate respondsToSelector:@selector(onRefresh)])
  181. {
  182. [self.delegate onRefresh];
  183. }
  184. }
  185. - (void)insert:(NSArray<NSIndexPath *> *)indexPaths animated:(BOOL)animated
  186. {
  187. if (!indexPaths.count)
  188. {
  189. return;
  190. }
  191. NSMutableArray *addIndexPathes = [NSMutableArray array];
  192. [indexPaths enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
  193. NSIndexPath *indexPath = [NSIndexPath indexPathForRow:[obj integerValue] inSection:0];
  194. [addIndexPathes addObject:indexPath];
  195. }];
  196. // [self.tableView beginUpdates];
  197. // [self.tableView insertRowsAtIndexPaths:addIndexPathes withRowAnimation:UITableViewRowAnimationBottom];
  198. // [self.tableView endUpdates];
  199. [self.tableView reloadData];
  200. [UIView animateWithDuration:0.25 delay:0 options:7 animations:^{
  201. [self resetLayout];
  202. } completion:nil];
  203. [self.tableView nim_scrollToBottom:YES];
  204. }
  205. - (void)remove:(NSArray<NSIndexPath *> *)indexPaths
  206. {
  207. [self.tableView beginUpdates];
  208. [self.tableView deleteRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationNone];
  209. [self.tableView endUpdates];
  210. NSInteger row = [self.tableView numberOfRowsInSection:0] - 1;
  211. if (row > 0)
  212. {
  213. NSIndexPath *indexPath = [NSIndexPath indexPathForRow:row inSection:0];
  214. [self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionBottom animated:YES];
  215. }
  216. }
  217. - (void)update:(NSIndexPath *)indexPath
  218. {
  219. NIMMessageCell *cell = (NIMMessageCell *)[self.tableView cellForRowAtIndexPath:indexPath];
  220. if (cell) {
  221. [self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
  222. CGFloat scrollOffsetY = self.tableView.contentOffset.y;
  223. [self.tableView setContentOffset:CGPointMake(self.tableView.contentOffset.x, scrollOffsetY) animated:NO];
  224. }
  225. }
  226. - (BOOL)canInsertChatroomMessages
  227. {
  228. return !self.tableView.isDecelerating && !self.tableView.isDragging;
  229. }
  230. @end