ContactGroupPickerViewController.m 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599
  1. // _____ _
  2. // |_ _| |_ _ _ ___ ___ _ __ __ _
  3. // | | | ' \| '_/ -_) -_) ' \/ _` |_
  4. // |_| |_||_|_| \___\___|_|_|_\__,_(_)
  5. //
  6. // Threema iOS Client
  7. // Copyright (c) 2015-2020 Threema GmbH
  8. //
  9. // This program is free software: you can redistribute it and/or modify
  10. // it under the terms of the GNU Affero General Public License, version 3,
  11. // as published by the Free Software Foundation.
  12. //
  13. // This program is distributed in the hope that it will be useful,
  14. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. // GNU Affero General Public License for more details.
  17. //
  18. // You should have received a copy of the GNU Affero General Public License
  19. // along with this program. If not, see <https://www.gnu.org/licenses/>.
  20. #import "ContactGroupPickerViewController.h"
  21. #import "ContactTableDataSource.h"
  22. #import "GroupTableDataSource.h"
  23. #import "RecentTableDataSource.h"
  24. #import "WorkContactTableDataSource.h"
  25. #import "BundleUtil.h"
  26. #import "RectUtil.h"
  27. #import "AppGroup.h"
  28. #import "LicenseStore.h"
  29. #import "PickerContactCell.h"
  30. #import "PickerGroupCell.h"
  31. #define LAST_SELECTED_MODE @"ContactGroupPickerLastSelectedMode"
  32. typedef enum : NSUInteger {
  33. ModeContact,
  34. ModeGroup,
  35. ModeRecent,
  36. ModeWorkContact
  37. } SelectionMode;
  38. @interface ContactGroupPickerViewController () <UITableViewDelegate, UISearchBarDelegate, UISearchResultsUpdating>
  39. @property SelectionMode mode;
  40. @property id<ContactGroupDataSource> currentDataSource;
  41. @property CGFloat searchBarHeight;
  42. @property BOOL isSearchBarHidden;
  43. @property BOOL isTextInputHidden;
  44. @end
  45. @implementation ContactGroupPickerViewController
  46. + (UIStoryboard *)contactPickerStoryboard {
  47. NSBundle *frameworkBundle = [BundleUtil frameworkBundle];
  48. return [UIStoryboard storyboardWithName:@"ContactPicker" bundle:frameworkBundle];
  49. }
  50. + (ModalNavigationController *)pickerFromStoryboardWithDelegate:(id<ModalNavigationControllerDelegate, ContactGroupPickerDelegate>)delegate {
  51. UIStoryboard *storyboard = [ContactGroupPickerViewController contactPickerStoryboard];
  52. ModalNavigationController *navigationController = [storyboard instantiateInitialViewController];
  53. navigationController.dismissOnTapOutside = NO;
  54. navigationController.modalDelegate = delegate;
  55. ContactGroupPickerViewController *picker = (ContactGroupPickerViewController *)[navigationController topViewController];
  56. picker.delegate = delegate;
  57. picker.enableMulitSelection = YES; //defaults to YES
  58. picker.enableTextInput = YES; //defaults to YES
  59. return navigationController;
  60. }
  61. -(void)dealloc {
  62. [[NSNotificationCenter defaultCenter] removeObserver: self];
  63. }
  64. - (void)viewDidLoad
  65. {
  66. [super viewDidLoad];
  67. WorkContactTableDataSource *workDataSource = [WorkContactTableDataSource workContactTableDataSource];
  68. if ([LicenseStore requiresLicenseKey] && workDataSource.countOfWorkContacts > 0) {
  69. [self.segmentedControl insertSegmentWithTitle:[BundleUtil localizedStringForKey:@"work"] atIndex:ModeWorkContact animated:NO];
  70. }
  71. NSUserDefaults *defaults = [AppGroup userDefaults];
  72. NSNumber *type = [defaults objectForKey:LAST_SELECTED_MODE];
  73. if (type) {
  74. _mode = type.integerValue;
  75. } else {
  76. _mode = ModeContact;
  77. }
  78. _sendAsFileSwitch.on = false;
  79. self.searchController = [[UISearchController alloc]initWithSearchResultsController:nil];
  80. self.searchController.searchBar.showsScopeBar = NO;
  81. self.searchController.searchBar.scopeButtonTitles = nil;
  82. self.tableView.keyboardDismissMode = UIScrollViewKeyboardDismissModeNone;
  83. self.searchController.searchBar.delegate = self;
  84. self.searchController.searchResultsUpdater = self;
  85. self.searchController.searchBar.autoresizingMask = UIViewAutoresizingFlexibleWidth;
  86. [self.searchController.searchBar sizeToFit];
  87. self.searchController.searchBar.barStyle = UISearchBarStyleMinimal;
  88. self.searchController.dimsBackgroundDuringPresentation = NO;
  89. self.definesPresentationContext = NO;
  90. if (@available(iOS 13.0, *)) {
  91. // iOS 13 and 13.1 have a bug. When searchbar was active, the navigationitem is not available
  92. // Bug should be fixed in 13.2
  93. if (@available(iOS 13.2, *)) {
  94. self.searchController.hidesNavigationBarDuringPresentation = false;
  95. } else {
  96. self.searchController.hidesNavigationBarDuringPresentation = true;
  97. }
  98. } else {
  99. self.searchController.hidesNavigationBarDuringPresentation = false;
  100. }
  101. if (@available(iOS 11.0, *)) {
  102. self.navigationItem.searchController = _searchController;
  103. self.navigationItem.hidesSearchBarWhenScrolling = NO;
  104. }
  105. _searchBarHeight = self.searchController.searchBar.frame.size.height;
  106. [self updateUIStrings];
  107. if (_submitOnSelect) {
  108. _controlView.hidden = YES;
  109. }
  110. _isTextInputHidden = YES;
  111. _tableView.dataSource = _currentDataSource;
  112. _tableView.delegate = self;
  113. _tableView.allowsMultipleSelection = _enableMulitSelection;
  114. [self registerForKeyboardNotifications];
  115. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(willResignActive:) name:UIApplicationWillResignActiveNotification object:nil];
  116. [self setupColors];
  117. self.tableView.estimatedRowHeight = UITableViewAutomaticDimension;
  118. self.tableView.rowHeight = UITableViewAutomaticDimension;
  119. CGRect frame = CGRectZero;
  120. frame.size.height = CGFLOAT_MIN;
  121. [self.tableView setTableHeaderView:[[UIView alloc] initWithFrame:frame]];
  122. }
  123. - (void)setupColors {
  124. [self.view setBackgroundColor:[Colors background]];
  125. _controlView.backgroundColor = [Colors backgroundDark];
  126. _buttonView.backgroundColor = [Colors backgroundDark];
  127. [_sendButton setTintColor:[Colors fontLink]];
  128. [_addTextButton setTintColor:[Colors fontLink]];
  129. [_hideTextButton setTintColor:[Colors fontLink]];
  130. [_textView setBackgroundColor:[Colors backgroundDark]];
  131. [_textView setTextColor:[Colors fontNormal]];
  132. [Colors updateTableView:self.tableView];
  133. [Colors updateSearchBar:_searchController.searchBar];
  134. [Colors updateKeyboardAppearanceFor:self.textView];
  135. [_hairLineView setBackgroundColor:[Colors hairline]];
  136. _sendAsFileLabel.textColor = [Colors fontLink];
  137. [self.navigationItem.leftBarButtonItem setTitleTextAttributes:[NSDictionary dictionaryWithObjectsAndKeys:
  138. [Colors main], NSForegroundColorAttributeName,
  139. nil] forState:UIControlStateNormal];
  140. [self.navigationItem.leftBarButtonItem setTitleTextAttributes:[NSDictionary dictionaryWithObjectsAndKeys:
  141. [Colors main], NSForegroundColorAttributeName,
  142. nil] forState:UIControlStateHighlighted];
  143. }
  144. - (void)viewWillAppear:(BOOL)animated {
  145. [super viewWillAppear:animated];
  146. self.navigationItem.titleView = _segmentedControl;
  147. self.segmentedControl.selectedSegmentIndex = _mode;
  148. [self segmentedControlChanged:nil];
  149. if (_renderType == nil) {
  150. _renderType = @0;
  151. }
  152. }
  153. - (void)viewDidAppear:(BOOL)animated {
  154. [super viewDidAppear:animated];
  155. if (_enableTextInput == NO) {
  156. _addTextButton.hidden = YES;
  157. }
  158. [self updateButtons];
  159. }
  160. - (void)viewWillDisappear:(BOOL)animated {
  161. [super viewWillDisappear:animated];
  162. NSUserDefaults *defaults = [AppGroup userDefaults];
  163. [defaults setValue:[NSNumber numberWithInteger:_mode] forKey:LAST_SELECTED_MODE];
  164. }
  165. - (void)setEnableMulitSelection:(BOOL)allowMulitSelection {
  166. _enableMulitSelection = allowMulitSelection;
  167. _tableView.allowsMultipleSelection = _enableMulitSelection;
  168. }
  169. - (void)updateUIStrings {
  170. // iOS 10 and 12 have different subviews sorting, so we have to check it with name and replace it at the end with the image
  171. [self.segmentedControl setTitle:@"contacts" forSegmentAtIndex:ModeContact];
  172. [self.segmentedControl setTitle:@"groups" forSegmentAtIndex:ModeGroup];
  173. [self.segmentedControl setTitle:@"recent" forSegmentAtIndex:ModeRecent];
  174. if ([LicenseStore requiresLicenseKey] && self.segmentedControl.numberOfSegments == 4) {
  175. [self.segmentedControl setTitle:@"work" forSegmentAtIndex:ModeWorkContact];
  176. }
  177. for (int i = 0; i < self.segmentedControl.numberOfSegments; i++) {
  178. UIView *segment = self.segmentedControl.subviews[i];
  179. for (id subview in segment.subviews) {
  180. if ([subview isKindOfClass:[UILabel class]]) {
  181. UILabel *label = (UILabel *)subview;
  182. if ([label.text isEqualToString:@"contacts"]) {
  183. segment.accessibilityLabel = [BundleUtil localizedStringForKey:@"contacts"];
  184. }
  185. else if ([label.text isEqualToString:@"groups"]) {
  186. segment.accessibilityLabel = [BundleUtil localizedStringForKey:@"groups"];
  187. }
  188. else if ([label.text isEqualToString:@"recent"]) {
  189. segment.accessibilityLabel = [BundleUtil localizedStringForKey:@"recent"];
  190. }
  191. else if ([label.text isEqualToString:@"work"]) {
  192. segment.accessibilityLabel = [BundleUtil localizedStringForKey:@"work"];
  193. }
  194. }
  195. }
  196. }
  197. [self.segmentedControl setTitle:nil forSegmentAtIndex:ModeContact];
  198. [self.segmentedControl setTitle:nil forSegmentAtIndex:ModeGroup];
  199. [self.segmentedControl setTitle:nil forSegmentAtIndex:ModeRecent];
  200. [self.segmentedControl setImage:[BundleUtil imageNamed:@"Contact"] forSegmentAtIndex:ModeContact];
  201. [self.segmentedControl setImage:[BundleUtil imageNamed:@"Group"] forSegmentAtIndex:ModeGroup];
  202. [self.segmentedControl setImage:[BundleUtil imageNamed:@"Recent"] forSegmentAtIndex:ModeRecent];
  203. if ([LicenseStore requiresLicenseKey] && self.segmentedControl.numberOfSegments == 4) {
  204. [self.segmentedControl setTitle:nil forSegmentAtIndex:ModeWorkContact];
  205. [self.segmentedControl setImage:[BundleUtil imageNamed:@"Case"] forSegmentAtIndex:ModeWorkContact];
  206. }
  207. [_addTextButton setTitle:[BundleUtil localizedStringForKey:@"addText"] forState:UIControlStateNormal];
  208. [_hideTextButton setTitle:[BundleUtil localizedStringForKey:@"hide"] forState:UIControlStateNormal];
  209. [_sendButton setTitle:[BundleUtil localizedStringForKey:@"send"]];
  210. [_sendAsFileLabel setText:[BundleUtil localizedStringForKey:@"send_as_file"]];
  211. }
  212. - (BOOL)shouldAutorotate {
  213. return YES;
  214. }
  215. -(UIInterfaceOrientationMask)supportedInterfaceOrientations {
  216. if (SYSTEM_IS_IPAD) {
  217. return UIInterfaceOrientationMaskAll;
  218. }
  219. return UIInterfaceOrientationMaskAllButUpsideDown;
  220. }
  221. # pragma mark - Keyboard Notifications
  222. - (void)registerForKeyboardNotifications {
  223. [[NSNotificationCenter defaultCenter] addObserver:self
  224. selector:@selector(keyboardWillShow:)
  225. name:UIKeyboardWillShowNotification object:nil];
  226. [[NSNotificationCenter defaultCenter] addObserver:self
  227. selector:@selector(keyboardWillHide:)
  228. name:UIKeyboardWillHideNotification object:nil];
  229. }
  230. - (void)keyboardWillShow:(NSNotification *)notification {
  231. [self processKeyboardNotification:notification willHide:NO];
  232. }
  233. - (void)keyboardWillHide:(NSNotification *)notification {
  234. [self processKeyboardNotification:notification willHide:YES];
  235. }
  236. - (void)processKeyboardNotification:(NSNotification*)notification willHide:(BOOL)willHide {
  237. NSDictionary* info = [notification userInfo];
  238. NSNumber *durationValue = info[UIKeyboardAnimationDurationUserInfoKey];
  239. NSTimeInterval animationDuration = durationValue.doubleValue;
  240. NSNumber *curveValue = info[UIKeyboardAnimationCurveUserInfoKey];
  241. UIViewAnimationCurve animationCurve = curveValue.intValue;
  242. CGRect keyboardRect = [info[UIKeyboardFrameEndUserInfoKey] CGRectValue];
  243. CGFloat keyboardHeight = willHide ? 0.0f : keyboardRect.size.height;
  244. [UIView animateWithDuration:animationDuration delay:0 options:(animationCurve << 16 | UIViewAnimationOptionBeginFromCurrentState) animations:^{
  245. CGFloat offset = 0.0;
  246. if (_isTextInputHidden == true) {
  247. offset = willHide == true ? 50.0 : keyboardHeight + _buttonView.frame.size.height;
  248. } else {
  249. offset = willHide == true ? 50.0 : keyboardHeight + _controlView.frame.size.height;
  250. }
  251. if (@available(iOS 11.0, *)) {
  252. float difference = self.view.safeAreaLayoutGuide.layoutFrame.size.height - self.view.frame.size.height;
  253. if (willHide == false) {
  254. offset += difference;
  255. }
  256. }
  257. _tableViewBottomConstraint.constant = offset;
  258. } completion:^(BOOL finished) {
  259. }];
  260. }
  261. #pragma mark - UIApplication Notifications
  262. - (void)willResignActive:(NSNotification *)notification {
  263. [self hideTextAction:nil];
  264. }
  265. #pragma mark - table view delegate
  266. - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
  267. return UITableViewAutomaticDimension;
  268. }
  269. -(CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath {
  270. return UITableViewAutomaticDimension;
  271. }
  272. - (void)tableView:(UITableView *)tableView willDisplayHeaderView:(nonnull UIView *)view forSection:(NSInteger)section {
  273. UITableViewHeaderFooterView *headerView = (UITableViewHeaderFooterView*)view;
  274. [headerView.contentView setBackgroundColor:[Colors backgroundDark]];
  275. [headerView.textLabel setTextColor:[Colors fontNormal]];
  276. }
  277. - (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
  278. if ([cell isKindOfClass:[UITableViewCell class]]) {
  279. [Colors updateTableViewCell:cell];
  280. UIView *selectedBgView = [[UIView alloc] init];
  281. selectedBgView.backgroundColor = [Colors shareExtensionSelectedBackground];
  282. [cell setSelectedBackgroundView:selectedBgView];
  283. }
  284. if ([cell isKindOfClass:[PickerContactCell class]]) {
  285. PickerContactCell *pickerContactCell = (PickerContactCell *)cell;
  286. BOOL found = false;
  287. for (Conversation *conversation in [_currentDataSource selectedConversations]) {
  288. if (conversation.contact != nil && conversation.contact == pickerContactCell.contact) {
  289. found = true;
  290. }
  291. }
  292. if (found == true) {
  293. [self.tableView selectRowAtIndexPath:indexPath animated:NO scrollPosition:UITableViewScrollPositionNone];
  294. }
  295. }
  296. if ([cell isKindOfClass:[PickerGroupCell class]]) {
  297. PickerGroupCell *pickerGroupCell = (PickerGroupCell *)cell;
  298. BOOL found = false;
  299. for (Conversation *conversation in [_currentDataSource selectedConversations]) {
  300. if (conversation.groupId != nil && [conversation.groupId isEqualToData:pickerGroupCell.group.groupId]) {
  301. found = true;
  302. }
  303. }
  304. if (found == true) {
  305. [self.tableView selectRowAtIndexPath:indexPath animated:NO scrollPosition:UITableViewScrollPositionNone];
  306. }
  307. }
  308. }
  309. - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
  310. [_currentDataSource selectedCellAtIndexPath:indexPath selected:YES];
  311. if (_submitOnSelect) {
  312. [self.delegate contactPicker:self didPickConversations:_currentDataSource.selectedConversations renderType:_renderType sendAsFile:_sendAsFileSwitch.on];
  313. } else {
  314. [self updateButtons];
  315. }
  316. }
  317. - (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath {
  318. [_currentDataSource selectedCellAtIndexPath:indexPath selected:NO];
  319. [self updateButtons];
  320. }
  321. - (void)updateButtons {
  322. NSUInteger count = [_currentDataSource selectedConversations].count;
  323. BOOL hasSelection = count > 0;
  324. _sendButton.enabled = hasSelection;
  325. if (hasSelection) {
  326. [_sendButton setTitle:[NSString stringWithFormat:@"%@ (%lu)", [BundleUtil localizedStringForKey:@"send"], (unsigned long)count]];
  327. } else {
  328. [_sendButton setTitle:[BundleUtil localizedStringForKey:@"send"]];
  329. }
  330. }
  331. - (void)hideSearchBar:(BOOL)hide {
  332. if (@available(iOS 11.0, *)) {
  333. if (_isSearchBarHidden == hide) {
  334. return;
  335. }
  336. _isSearchBarHidden = hide;
  337. [UIView animateWithDuration:0.3 animations:^{
  338. [self.searchController.searchBar setHidden:hide];
  339. }];
  340. }
  341. }
  342. - (void)hideTextInput:(BOOL)hide {
  343. if (_isTextInputHidden == hide) {
  344. return;
  345. }
  346. if (hide) {
  347. [self updateAddButtonTitle];
  348. }
  349. _isTextInputHidden = hide;
  350. [UIView animateWithDuration:0.3 animations:^{
  351. _hideTextButton.hidden = hide;
  352. _addTextButton.hidden = !hide;
  353. _textView.hidden = hide;
  354. }];
  355. }
  356. - (void)updateAddButtonTitle {
  357. NSString *addButtonTitle;
  358. if ([self hasAdditionalText]) {
  359. _addTextButton.frame = [RectUtil setWidthOf:_addTextButton.frame width:150.0];
  360. addButtonTitle = [self trimmedText];
  361. } else {
  362. addButtonTitle = [BundleUtil localizedStringForKey:@"addText"];
  363. }
  364. [_addTextButton setTitle:addButtonTitle forState:UIControlStateNormal];
  365. }
  366. - (NSString *)additionalTextToSend {
  367. if ([self hasAdditionalText]) {
  368. return [self trimmedText];
  369. } else {
  370. return nil;
  371. }
  372. }
  373. - (BOOL)hasAdditionalText {
  374. return [self trimmedText].length > 0;
  375. }
  376. - (NSString *)trimmedText {
  377. return [_textView.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
  378. }
  379. - (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
  380. [_textView resignFirstResponder];
  381. [_searchController.searchBar resignFirstResponder];
  382. [self hideTextInput:YES];
  383. }
  384. #pragma mark - Actions
  385. - (IBAction)addTextAction:(id)sender {
  386. [self hideTextInput:NO];
  387. if (@available(iOS 11.0, *)) {
  388. } else {
  389. [self hideSearchBar:YES];
  390. }
  391. [_textView becomeFirstResponder];
  392. _controlView.backgroundColor = [Colors background];
  393. _textView.backgroundColor = [Colors background];
  394. }
  395. - (IBAction)hideTextAction:(id)sender {
  396. [self hideTextInput:YES];
  397. if (_mode != ModeRecent) {
  398. [self hideSearchBar:NO];
  399. }
  400. [_textView resignFirstResponder];
  401. _controlView.backgroundColor = _buttonView.backgroundColor;
  402. _textView.backgroundColor = _buttonView.backgroundColor;
  403. }
  404. - (IBAction)cancelAction:(id)sender {
  405. [self.delegate contactPickerDidCancel:self];
  406. }
  407. - (IBAction)doneAction:(id)sender {
  408. [self.searchController setActive:false];
  409. [self.delegate contactPicker:self didPickConversations:_currentDataSource.selectedConversations renderType:_renderType sendAsFile:_sendAsFileSwitch.on];
  410. }
  411. - (IBAction)segmentedControlChanged:(id)sender {
  412. _mode = self.segmentedControl.selectedSegmentIndex;
  413. [_textView resignFirstResponder];
  414. [self hideTextInput:YES];
  415. switch (_mode) {
  416. case ModeContact:
  417. [self hideSearchBar:NO];
  418. _currentDataSource = [ContactTableDataSource contactTableDataSource];
  419. [_currentDataSource filterByWords: [self searchWordsForText:_searchController.searchBar.text]];
  420. break;
  421. case ModeGroup:
  422. [self hideSearchBar:NO];
  423. _currentDataSource = [GroupTableDataSource groupTableDataSource];
  424. [_currentDataSource filterByWords: [self searchWordsForText:_searchController.searchBar.text]];
  425. break;
  426. case ModeRecent:
  427. [self hideSearchBar:YES];
  428. _currentDataSource = [RecentTableDataSource recentTableDataSource];
  429. break;
  430. case ModeWorkContact:
  431. [self hideSearchBar:NO];
  432. _currentDataSource = [WorkContactTableDataSource workContactTableDataSource];
  433. [_currentDataSource filterByWords: [self searchWordsForText:_searchController.searchBar.text]];
  434. break;
  435. default:
  436. break;
  437. }
  438. [self updateButtons];
  439. _tableView.dataSource = _currentDataSource;
  440. [self.tableView reloadData];
  441. }
  442. #pragma mark - Scroll view delegate
  443. - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
  444. [_searchController.searchBar resignFirstResponder];
  445. [_textView resignFirstResponder];
  446. [self hideTextInput:YES];
  447. if (_mode != ModeRecent) {
  448. [self hideSearchBar:NO];
  449. }
  450. }
  451. #pragma mark - Search bar delegate
  452. - (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText {
  453. NSArray *searchWords = [self searchWordsForText:searchText];
  454. [_currentDataSource filterByWords: searchWords];
  455. [self.tableView reloadData];
  456. }
  457. -(void)updateSearchResultsForSearchController:(UISearchController *)searchController {
  458. NSArray *searchWords = [self searchWordsForText:_searchController.searchBar.text];
  459. [_currentDataSource filterByWords: searchWords];
  460. [self.tableView reloadData];
  461. }
  462. - (NSArray *)searchWordsForText:(NSString *)text {
  463. NSArray *searchWords = nil;
  464. if (text && [text length] > 0) {
  465. searchWords = [text componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
  466. }
  467. return searchWords;
  468. }
  469. @end