I am trying to use the displaysSearchBarInNavigationBar
property in the iOS7 UISearchDisplayController
class to display the search bar inside a navigation bar.
Using the AdvancedTableSearch example from Apple as a base, I've changed the code to disable scopes (which are not allowed in the navigation bar) and set displaysSearchBarInNavigationBar
to true, like so.
- (void)viewDidLoad
{
[super viewDidLoad];
// create a mutable array to contain products for the search results table
self.searchResults = [NSMutableArray arrayWithCapacity:[self.products count]];
self.searchDisplayController.displaysSearchBarInNavigationBar = YES;
}
Unfortunately the result I am getting looks like this:
The search bar appears in the middle of the screen instead of in the navigationItem.
What am I doing wrong?
PS: I'm not sure if it's related, but self.searchDisplayController.navigationItem
property is nil
.
Remove the search bar from the table view. Using displaysSearchBarInNavigationBar
means that UISearchDisplayController will take care of placing the search bar in the hierarchy for you.
Also, navigationItem
will be nil until displaysSearchBarInNavigationBar
is set to YES. The item is created only when needed.
I was having the same problem as you were and spent hours looking for a solution. Ultimately I went with creating the UISearchDisplayController
programatically.
I create a SearchTableViewController
in the Storyboard
and then do the rest of the work programatically. The controllers delegate must be included in the header, as you will notice in the code provided below. Now the interesting thing happens when I create the searchBar
. Notice how I never set its delegate? The UISearchDisplayController
is doing that for me when I create it with the searchBar
. Then I only need to set the delegate and source for the UISearchDislpayController
and for the searchResults
; which I do right after creating the controller. I cannot provide you with the answer as to "WHY" the searchBar
gets centered in the view when created in the Storyboard
and set to displaysSearchBarInNavigationBar:YES
in code, but I had the same problem and found the following to be a viable solution. Especially considering I never have to size anything :)
SearchTableViewController.h
#import <UIKit/UIKit.h>
@interface SearchableTableViewController : UITableViewController<UISearchDisplayDelegate>
//I only need the SearchDisplayController Delegate because it magically has all the needed child delegates. :)
@end
SearchTableViewController.m
#import "SearchDisplayController.h"
@property (strong,nonatomic) IBOutlet UITableView *acSearchTableView;
@property (retain,nonatomic) UISearchBar *acSearchBar;
@property (retain,nonatomic) UISearchDisplayController *searchDsplyCntrl;
@property (strong,nonatomic) NSArray *unfilteredResults;
@property (strong,nonatomic) NSMutableArray *filteredResults;
@implementation SearchTableViewController
- (void) viewDidLoad {
[super viewDidLoad];
_acSearchBar = [[UISearchBar alloc]init];
_acSearchBar.showsCancelButton = NO;
/* NOTE: by default the placholer is centered. It can be left aligned with spaces */
_acSearchBar.placeholder = @"Search ";
_searchDsplyCntrl = [[UISearchDisplayController alloc]initWithSearchBar:_acSearchBar contentsController:self];
_searchDsplyCntrl.delegate = self;
_searchDsplyCntrl.searchResultsDelegate = self;
_searchDsplyCntrl.searchResultsDataSource = self;
_searchDsplyCntrl.displaysSearchBarInNavigationBar = YES;
_unfilteredResults = [[NSArray alloc]initWithObjects:
[ResultObj resultWithName:@"first"],
[ResultObj resultWithName:@"second"],
[ResultObj resultWithName:@"third"],
[ResultObj resultWithName:@"forth"],
[ResultObj resultWithName:@"fifth"],
[ResultObj resultWithName:@"sixth"],
[ResultObj resultWithName:@"seventh"],
[ResultObj resultWithName:@"eigth"],nil];
_filteredResults = [NSMutableArray arrayWithCapacity:[_unfilteredResults count]];
[_acSearchTableView reloadData];
}
- (void)searchDisplayControllerWillBeginSearch:(UISearchDisplayController *)controller {
[_searchDsplyCntrl setActive:YES animated:YES];
[_searchDsplyCntrl.searchBar setShowsCancelButton:YES animated:YES];
}
- (void)searchDisplayControllerWillEndSearch:(UISearchDisplayController *)controller {
[_searchDsplyCntrl setActive:NO animated:YES];
[_searchDsplyCntrl.searchBar setShowsCancelButton:NO animated:YES];
}
A search bar displayed in a navigation bar cannot have a scope bar.
IMPORTANT The system raises an exception if you set the showsScopeBar property to YES in a search bar that is displayed in a navigation bar.
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if (tableView == _searchDsplyCntrl.searchResultsTableView)
{
return [_filteredResults count];
}
else
{
return [_unfilteredResults count];
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if ( cell == nil ) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
// Create a new Candy Object
ResultObj *result = nil;
// Check to see whether the normal table or search results table is being displayed and set the Candy object from the appropriate array
if (tableView == _searchDsplyCntrl.searchResultsTableView)
{
result = [_filteredResults objectAtIndex:[indexPath row]];
}
else
{
result = [_unfilteredResults objectAtIndex:[indexPath row]];
}
// Configure the cell
[[cell textLabel] setText:[result name]];
[cell setAccessoryType:UITableViewCellAccessoryDisclosureIndicator];
return cell;
}
#pragma mark Content Filtering
- (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope
{
// Update the filtered array based on the search text and scope.
// Remove all objects from the filtered search array
[_filteredResults removeAllObjects];
// Filter the array using NSPredicate
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF.name contains[c] %@",searchText];
NSArray *tempArray = [_unfilteredResults filteredArrayUsingPredicate:predicate];
/*
if(![scope isEqualToString:@"All"]) {
// Further filter the array with the scope
NSPredicate *scopePredicate = [NSPredicate predicateWithFormat:@"SELF.category contains[c] %@",scope];
tempArray = [tempArray filteredArrayUsingPredicate:scopePredicate];
}
*/
_filteredResults = [NSMutableArray arrayWithArray:tempArray];
}
#pragma mark - UISearchDisplayController Delegate Methods
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString
{
// Tells the table data source to reload when text changes
[self filterContentForSearchText:searchString scope:
[[_searchDsplyCntrl.searchBar scopeButtonTitles] objectAtIndex:[_searchDsplyCntrl.searchBar selectedScopeButtonIndex]]];
// Return YES to cause the search result table view to be reloaded.
return YES;
}
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchScope:(NSInteger)searchOption
{
// Tells the table data source to reload when scope bar selection changes
[self filterContentForSearchText:[_searchDsplyCntrl.searchBar text] scope:
[[_searchDsplyCntrl.searchBar scopeButtonTitles] objectAtIndex:searchOption]];
// Return YES to cause the search result table view to be reloaded.
return YES;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With