FLEXTableViewController.m 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. //
  2. // FLEXTableViewController.m
  3. // FLEX
  4. //
  5. // Created by Tanner on 7/5/19.
  6. // Copyright © 2019 Flipboard. All rights reserved.
  7. //
  8. #import "FLEXTableViewController.h"
  9. @interface Block : NSObject
  10. - (void)invoke;
  11. @end
  12. CGFloat const kFLEXDebounceInstant = 0.f;
  13. CGFloat const kFLEXDebounceFast = 0.05;
  14. CGFloat const kFLEXDebounceForAsyncSearch = 0.15;
  15. CGFloat const kFLEXDebounceForExpensiveIO = 0.5;
  16. @interface FLEXTableViewController ()
  17. @property (nonatomic) NSTimer *debounceTimer;
  18. @end
  19. @implementation FLEXTableViewController
  20. #pragma mark - Public
  21. - (id)init {
  22. #if FLEX_AT_LEAST_IOS13_SDK
  23. if (@available(iOS 13.0, *)) {
  24. self = [self initWithStyle:UITableViewStyleInsetGrouped];
  25. } else {
  26. self = [self initWithStyle:UITableViewStyleGrouped];
  27. }
  28. #else
  29. self = [self initWithStyle:UITableViewStyleGrouped];
  30. #endif
  31. return self;
  32. }
  33. - (id)initWithStyle:(UITableViewStyle)style {
  34. self = [super initWithStyle:style];
  35. if (self) {
  36. self.searchBarDebounceInterval = kFLEXDebounceFast;
  37. }
  38. return self;
  39. }
  40. - (void)setShowsSearchBar:(BOOL)showsSearchBar {
  41. if (_showsSearchBar == showsSearchBar) return;
  42. _showsSearchBar = showsSearchBar;
  43. UIViewController *results = self.searchResultsController;
  44. self.searchController = [[UISearchController alloc] initWithSearchResultsController:results];
  45. self.searchController.searchBar.placeholder = @"Filter";
  46. self.searchController.searchResultsUpdater = (id)self;
  47. self.searchController.delegate = (id)self;
  48. self.searchController.dimsBackgroundDuringPresentation = NO;
  49. self.searchController.hidesNavigationBarDuringPresentation = NO;
  50. /// Not necessary in iOS 13; remove this when iOS 13 is the deployment target
  51. self.searchController.searchBar.delegate = self;
  52. if (@available(iOS 11.0, *)) {
  53. self.navigationItem.searchController = self.searchController;
  54. } else {
  55. self.tableView.tableHeaderView = self.searchController.searchBar;
  56. }
  57. }
  58. - (NSInteger)selectedScope {
  59. return self.searchController.searchBar.selectedScopeButtonIndex;
  60. }
  61. - (NSString *)searchText {
  62. return self.searchController.searchBar.text;
  63. }
  64. - (void)setAutomaticallyShowsSearchBarCancelButton:(BOOL)autoShowCancel {
  65. #if FLEX_AT_LEAST_IOS13_SDK
  66. if (@available(iOS 13, *)) {
  67. self.searchController.automaticallyShowsCancelButton = autoShowCancel;
  68. } else {
  69. _automaticallyShowsSearchBarCancelButton = autoShowCancel;
  70. }
  71. #else
  72. _automaticallyShowsSearchBarCancelButton = autoShowCancel;
  73. #endif
  74. }
  75. - (void)updateSearchResults:(NSString *)newText { }
  76. - (void)onBackgroundQueue:(NSArray *(^)())backgroundBlock thenOnMainQueue:(void(^)(NSArray *))mainBlock {
  77. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
  78. NSArray *items = backgroundBlock();
  79. dispatch_async(dispatch_get_main_queue(), ^{
  80. mainBlock(items);
  81. });
  82. });
  83. }
  84. #pragma mark - View Controller Lifecycle
  85. - (void)viewDidLoad {
  86. [super viewDidLoad];
  87. self.tableView.keyboardDismissMode = UIScrollViewKeyboardDismissModeOnDrag;
  88. }
  89. - (void)viewWillAppear:(BOOL)animated {
  90. [super viewWillAppear:animated];
  91. // Make the search bar re-appear instead of hiding
  92. if (@available(iOS 11.0, *)) if (!self.hideSearchBarInitially) {
  93. self.navigationItem.hidesSearchBarWhenScrolling = NO;
  94. }
  95. }
  96. - (void)viewDidAppear:(BOOL)animated {
  97. [super viewDidAppear:animated];
  98. // Allow scrolling to collapse the search bar,
  99. // only if we don't want it pinned
  100. if (@available(iOS 11.0, *)) if (!self.hideSearchBarInitially) {
  101. self.navigationItem.hidesSearchBarWhenScrolling = !self.pinSearchBar;
  102. }
  103. }
  104. #pragma mark - Private
  105. - (void)debounce:(void(^)())block {
  106. [self.debounceTimer invalidate];
  107. self.debounceTimer = [NSTimer
  108. scheduledTimerWithTimeInterval:self.searchBarDebounceInterval
  109. target:block
  110. selector:@selector(invoke)
  111. userInfo:nil
  112. repeats:NO
  113. ];
  114. }
  115. #pragma mark - Search Bar
  116. #pragma mark UISearchResultsUpdating
  117. - (void)updateSearchResultsForSearchController:(UISearchController *)searchController
  118. {
  119. [self.debounceTimer invalidate];
  120. NSString *text = searchController.searchBar.text;
  121. // Only debounce if we want to, and if we have a non-empty string
  122. // Empty string events are sent instantly
  123. if (text.length && self.searchBarDebounceInterval > kFLEXDebounceInstant) {
  124. [self debounce:^{
  125. [self updateSearchResults:text];
  126. }];
  127. } else {
  128. [self updateSearchResults:text];
  129. }
  130. }
  131. #pragma mark UISearchControllerDelegate
  132. - (void)willPresentSearchController:(UISearchController *)searchController {
  133. if (self.automaticallyShowsSearchBarCancelButton) {
  134. [searchController.searchBar setShowsCancelButton:YES animated:YES];
  135. }
  136. }
  137. - (void)willDismissSearchController:(UISearchController *)searchController {
  138. if (self.automaticallyShowsSearchBarCancelButton) {
  139. [searchController.searchBar setShowsCancelButton:NO animated:YES];
  140. }
  141. }
  142. #pragma mark UISearchBarDelegate
  143. /// Not necessary in iOS 13; remove this when iOS 13 is the deployment target
  144. - (void)searchBar:(UISearchBar *)searchBar selectedScopeButtonIndexDidChange:(NSInteger)selectedScope {
  145. [self updateSearchResultsForSearchController:self.searchController];
  146. }
  147. @end