Просмотр исходного кода

Restore search bar appearing initially

In c047fbc5 I made it so that the search bar would not appear initially—except of of course for the globals screen in iOS 13, where it shows by default no matter what. We did this by toggling navigationItem.hidesSearchBarWhenScrolling in `viewWillAppear:` and `viewDidAppear:`.

The method we were using to reveal the search bar initially while letting it hide as you scroll would cause a weird visual glitch where the search bar would stay pinned to the top of the screen as you scroll, but the navigation bar would be transparent.

I've managed to work around that bug by calling `setNeedsLayout` and `layoutIfNeeded` on `self.navigationController.view`
Tanner Bennett лет назад: 6
Родитель
Сommit
fe36b59b4c

+ 8 - 1
Classes/Core/FLEXTableViewController.h

@@ -39,6 +39,11 @@ extern CGFloat const kFLEXDebounceForExpensiveIO;
 /// 
 /// Setting this to YES will initialize searchController and the view.
 @property (nonatomic) BOOL showsSearchBar;
+/// Defaults to NO.
+///
+/// Setting this to YES will make the search bar appear whenever the view appears.
+/// Otherwise, iOS will only show the search bar when you scroll up.
+@property (nonatomic) BOOL showSearchBarInitially;
 
 /// nil unless showsSearchBar is set to YES.
 /// 
@@ -66,7 +71,9 @@ extern CGFloat const kFLEXDebounceForExpensiveIO;
 /// search becomes active and hide it when search is dismissed.
 /// 
 /// Do not set the showsCancelButton property on the searchController's
-/// searchBar manually.
+/// searchBar manually. Set this property after turning on showsSearchBar.
+///
+/// Does nothing pre-iOS 13, safe to call on any version.
 @property (nonatomic) BOOL automaticallyShowsSearchBarCancelButton;
 
 /// If using the scope bar, self.searchController.searchBar.selectedScopeButtonIndex.

+ 81 - 14
Classes/Core/FLEXTableViewController.m

@@ -23,10 +23,11 @@ CGFloat const kFLEXDebounceForExpensiveIO = 0.5;
 
 @interface FLEXTableViewController ()
 @property (nonatomic) NSTimer *debounceTimer;
-
+@property (nonatomic) BOOL didInitiallyRevealSearchBar;
 @end
 
 @implementation FLEXTableViewController
+@synthesize automaticallyShowsSearchBarCancelButton = _automaticallyShowsSearchBarCancelButton;
 
 #pragma mark - Public
 
@@ -47,7 +48,8 @@ CGFloat const kFLEXDebounceForExpensiveIO = 0.5;
     self = [super initWithStyle:style];
     
     if (self) {
-        self.searchBarDebounceInterval = kFLEXDebounceFast;
+        _searchBarDebounceInterval = kFLEXDebounceFast;
+        _showSearchBarInitially = YES;
     }
     
     return self;
@@ -64,8 +66,10 @@ CGFloat const kFLEXDebounceForExpensiveIO = 0.5;
     self.searchController.delegate = (id)self;
     self.searchController.dimsBackgroundDuringPresentation = NO;
     self.searchController.hidesNavigationBarDuringPresentation = NO;
-    /// Not necessary in iOS 13; remove this when iOS 13 is the deployment target
+    /// Not necessary in iOS 13; remove this when iOS 13 is the minimum deployment target
     self.searchController.searchBar.delegate = self;
+
+    self.automaticallyShowsSearchBarCancelButton = YES;
     
     if (@available(iOS 11.0, *)) {
         self.navigationItem.searchController = self.searchController;
@@ -114,16 +118,24 @@ CGFloat const kFLEXDebounceForExpensiveIO = 0.5;
     return self.searchController.searchBar.text;
 }
 
-- (void)setAutomaticallyShowsSearchBarCancelButton:(BOOL)autoShowCancel {
+- (BOOL)automaticallyShowsSearchBarCancelButton {
 #if FLEX_AT_LEAST_IOS13_SDK
-    if (@available(iOS 13.0, *)) {
-        self.searchController.automaticallyShowsCancelButton = autoShowCancel;
-    } else {
-        _automaticallyShowsSearchBarCancelButton = autoShowCancel;
+    if (@available(iOS 13, *)) {
+        return self.searchController.automaticallyShowsCancelButton;
+    }
+#endif
+
+    return _automaticallyShowsSearchBarCancelButton;
+}
+
+- (void)setAutomaticallyShowsSearchBarCancelButton:(BOOL)value {
+#if FLEX_AT_LEAST_IOS13_SDK
+    if (@available(iOS 13, *)) {
+        self.searchController.automaticallyShowsCancelButton = value;
     }
-#else
-    _automaticallyShowsSearchBarCancelButton = autoShowCancel;
 #endif
+
+    _automaticallyShowsSearchBarCancelButton = value;
 }
 
 - (void)updateSearchResults:(NSString *)newText { }
@@ -143,17 +155,59 @@ CGFloat const kFLEXDebounceForExpensiveIO = 0.5;
     [super viewDidLoad];
     
     self.tableView.keyboardDismissMode = UIScrollViewKeyboardDismissModeOnDrag;
+
+    // On iOS 13, the root view controller shows it's search bar no matter what.
+    // Turning this off avoids some weird flash the navigation bar does when we
+    // toggle navigationItem.hidesSearchBarWhenScrolling on and off. The flash
+    // will still happen on subsequent view controllers, but we can at least
+    // avoid it for the root view controller
+    if (@available(iOS 13, *)) {
+        if (self.navigationController.viewControllers.firstObject == self) {
+            _showSearchBarInitially = NO;
+        }
+    }
 }
 
 - (void)viewWillAppear:(BOOL)animated {
     [super viewWillAppear:animated];
     
     // When going back, make the search bar reappear instead of hiding
-    if (@available(iOS 11.0, *)) if (self.pinSearchBar) {
-        self.navigationItem.hidesSearchBarWhenScrolling = NO;
+    if (@available(iOS 11.0, *)) {
+        if ((self.pinSearchBar || self.showSearchBarInitially) && !self.didInitiallyRevealSearchBar) {
+            self.navigationItem.hidesSearchBarWhenScrolling = NO;
+        }
     }
 }
 
+- (void)viewDidAppear:(BOOL)animated {
+    [super viewDidAppear:animated];
+
+    // Allow scrolling to collapse the search bar, only if we don't want it pinned
+    if (@available(iOS 11.0, *)) {
+        if (self.showSearchBarInitially && !self.pinSearchBar && !self.didInitiallyRevealSearchBar) {
+            // All this mumbo jumbo is necessary to work around a bug in iOS 13 up to 13.2
+            // wherein quickly toggling navigationItem.hidesSearchBarWhenScrolling to make
+            // the search bar appear initially results in a bugged search bar that
+            // becomes transparent and floats over the screen as you scroll
+            [UIView animateWithDuration:0.2 animations:^{
+                self.navigationItem.hidesSearchBarWhenScrolling = YES;
+                [self.navigationController.view setNeedsLayout];
+                [self.navigationController.view layoutIfNeeded];
+            }];
+        }
+    }
+
+    // We only want to reveal the search bar when the view controller first appears.
+    self.didInitiallyRevealSearchBar = YES;
+}
+
+- (void)willMoveToParentViewController:(UIViewController *)parent {
+    [super willMoveToParentViewController:parent];
+    // Reset this since we are re-appearing under a new
+    // parent view controller and need to show it again
+    self.didInitiallyRevealSearchBar = NO;
+}
+
 - (void)viewDidDisappear:(BOOL)animated {
     [super viewDidDisappear:animated];
 
@@ -199,13 +253,15 @@ CGFloat const kFLEXDebounceForExpensiveIO = 0.5;
 #pragma mark UISearchControllerDelegate
 
 - (void)willPresentSearchController:(UISearchController *)searchController {
-    if (self.automaticallyShowsSearchBarCancelButton) {
+    // Manually show cancel button for < iOS 13
+    if (!@available(iOS 13, *) && self.automaticallyShowsSearchBarCancelButton) {
         [searchController.searchBar setShowsCancelButton:YES animated:YES];
     }
 }
 
 - (void)willDismissSearchController:(UISearchController *)searchController {
-    if (self.automaticallyShowsSearchBarCancelButton) {
+    // Manually hide cancel button for < iOS 13
+    if (!@available(iOS 13, *) && self.automaticallyShowsSearchBarCancelButton) {
         [searchController.searchBar setShowsCancelButton:NO animated:YES];
     }
 }
@@ -217,4 +273,15 @@ CGFloat const kFLEXDebounceForExpensiveIO = 0.5;
     [self updateSearchResultsForSearchController:self.searchController];
 }
 
+#pragma mark Table view
+
+/// Not having a title in the first section looks weird with a rounded-corner table view style
+- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
+    if (@available(iOS 13, *)) {
+        return @" "; // For inset grouped style
+    }
+
+    return nil; // For plain/gropued style
+}
+
 @end

+ 2 - 0
Classes/ObjectExplorers/Views/FLEXTableView.h

@@ -10,6 +10,8 @@
 
 @interface FLEXTableView : UITableView
 
++ (instancetype)flexDefaultTableView;
+
 @property (nonatomic, readonly) NSString *defaultReuseIdentifier;
 @property (nonatomic, readonly) NSString *subtitleReuseIdentifier;
 @property (nonatomic, readonly) NSString *multilineReuseIdentifier;

+ 21 - 2
Classes/ObjectExplorers/Views/FLEXTableView.m

@@ -7,19 +7,38 @@
 //
 
 #import "FLEXTableView.h"
+#import "FLEXUtility.h"
 #import "FLEXSubtitleTableViewCell.h"
 #import "FLEXMultilineTableViewCell.h"
 
 @interface UITableView (Private)
 - (CGFloat)_heightForHeaderInSection:(NSInteger)section;
+- (NSString *)_titleForHeaderInSection:(NSInteger)section;
 @end
 
 @implementation FLEXTableView
 
++ (instancetype)flexDefaultTableView {
+#if FLEX_AT_LEAST_IOS13_SDK
+    if (@available(iOS 13.0, *)) {
+        return [[self alloc] initWithFrame:CGRectZero style:UITableViewStyleInsetGrouped];
+    } else {
+        return [[self alloc] initWithFrame:CGRectZero style:UITableViewStyleGrouped];
+    }
+#else
+    return [[self alloc] initWithFrame:CGRectZero style:UITableViewStyleGrouped];
+#endif
+}
+
 - (CGFloat)_heightForHeaderInSection:(NSInteger)section {
     CGFloat height = [super _heightForHeaderInSection:section];
-    if (section == 0 && self.tableHeaderView && !@available(iOS 13.0, *)) {
-        return height - self.tableHeaderView.frame.size.height + 8;
+    if (section == 0 && self.tableHeaderView) {
+        NSString *title = [self _titleForHeaderInSection:section];
+        if (!@available(iOS 13, *)) {
+            return height - self.tableHeaderView.frame.size.height + 8;
+        } else if ([title isEqualToString:@" "]) {
+            return height - self.tableHeaderView.frame.size.height + 5;
+        }
     }
 
     return height;