Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UISearchDisplayController.displaysSearchBarInNavigationBar positions the search bar in the middle of the window

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:

search bar in the wrong place

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.

like image 740
Andrew Newdigate Avatar asked Sep 24 '13 11:09

Andrew Newdigate


2 Answers

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.

like image 138
MyztikJenz Avatar answered Sep 18 '22 14:09

MyztikJenz


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;
}
like image 38
Ryan Alexander Avatar answered Sep 19 '22 14:09

Ryan Alexander