4
votes

I'm trying to get a search bar and it's scope in the navigation bar, as the way the view first appears. If you simply drag a UISearchBar onto you table view controller in interface builder, it's placed in your table like a header cell. Then, when you tap it, it animates into the formation I'm after below. The problem is I want it to start out this way without any tapping, with a back button on the left and no cancel button on the right:

enter image description here

So, to give the search bar immediate focus without tapping, I tried adding [_searchBar becomeFirstResponder]; in viewDidLoad which doesn't work. Next I tried:

self.searchDisplayController.displaysSearchBarInNavigationBar = YES;

The search bar will indeed display in the navigation bar but the scope bar is gone, (WHY APPLE WHY) and there is an awkward gap.

enter image description here

I've also tried subclassing UINavigationBar and making it taller. It's easy to change it's size but the contents align at the bottom of the bar and overlap anything you try to add to it.

So to re-iterate, I need the search bar to display in the navigation bar with a scope control when the view first appears, without any tapping by the user. I should also specify that this is on a UITableViewController (because the page has a UIRefreshConrol), so simply dropping a toolbar in above the table view isn't an option. Thanks.

1

1 Answers

4
votes

tl;dr: Don't.

You should know that adding a UISearchBar to the headerView of a UITableView invokes some custom code within UIKit that facilitates hiding the scope bar on start. While it used to be easier to show the scope bar all the time in iOS 6 and older, iOS 7 has changed this.

Your first approach, to becomeFirstResponder in viewDidLoad was a good idea, but this method is called after the view has been loaded into memory (such as out of a NIB). The view has not yet been added to the view hierarchy, so it can't become the first responder. viewDidAppear: is called right after the view is loaded into the view hierarchy, and a becomeFirstResponder does in fact allow the UISearchBar to receive focus.

The scope bar itself is a hidden (by default) subview of UISearchBar. You technically could just cycle through subviews and set it to not be hidden:

- (void)viewDidLoad
{
    [super viewDidLoad];
    [self recurseSubviewsForView:self.searchDisplayController.searchBar];
}

- (void)recurseSubviewsForView:(UIView *)view
{
    for (UIView *subview in view.subviews) {
        if ((CGRectGetMinY(subview.frame) == CGRectGetMaxY(self.searchDisplayController.searchBar.frame)) && subview.hidden) {
            subview.hidden = NO;
            self.searchDisplayController.searchBar.showsScopeBar = YES;
            [self.searchDisplayController.searchBar sizeToFit];
        }
        [self recurseSubviewsForView:subview];
    }
}

This is generally a bad idea. There's a basic check to see that the subview being targeted is the subview containing the scope bar, but that could break any second, might not be backwards compatible, etc.

The real question you have to ask yourself is why do I want the scope bar to always be visible? The way it works now, the scope bar will animate visible when the user taps into the search field and hide itself when the search field no longer has the focus. Even with the "hack" above, the search bar gets the focus as soon as the user taps a scope button. What's the point of showing a search option when search isn't even active? When in doubt, fall back to Apple's suggestions. It will make your life a lot easier in the long run, and probably make your app look and behave better. Apple didn't just add that interaction on a whim. They tested it like crazy. That's why it's so hard to make it work another way. That's why there's special code run when a UISearchBar is the headerView of a UITableView...because it's likely better for the user.

In addition, I see your screenshot targeting iOS 8, but your code snipped shows using a UISearchDisplayController. This paradigm has been deprecated in favor of UISearchController. Please consider updating.