Question

I have tried to implement a search bar but I have not had any luck dealing with this problem. I would really appreciate any help that can be provided. I've a big project in which I've a table view, and I want to implement a search bar over it and see the real time filtering. I do not use Storyboard but I'm using XIB. I've added the following protocols:

<UITableViewDelegate,UITableViewDataSource,UISearchBarDelegate,UISearchDisplayDelegate>

I've declared 2 arrays in @interface , the first for the whole elements and the second one for the filtered ones:

    NSArray*     OldList;
    NSArray*     filteredList;

Then I've setted the number of rows and the number of sections and then:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    myClassCell *cell = [tableView dequeueReusableCellWithIdentifier:MYCELLCLASS];
    if (cell == nil)
    {
        cell = [myClassCell newFromNib];
    }
    NSMutableDictionary* elem = nil;
    if (tableView == self.searchDisplayController.searchResultsTableView)
    {
        elem = [filteredList objectAtIndex:indexPath.row];
        if ([elem count]+1 > indexPath.row)
            [cell showValues:elem];
        else
            [cell showValues:nil];
    }
    else
    {
        elem = [OldList objectAtIndex:indexPath.row];
        if ([elem count]+1 > indexPath.row) 
            [cell showValues:elem];
        else
            [cell showValues:nil];
    }
    return cell;
}

-(void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope
{
    NSPredicate *resultPredicate = [NSPredicate predicateWithFormat:@"name contains[c] %@", searchText];
    filteredist = [OldList filteredArrayUsingPredicate:resultPredicate];
}

-(BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString
{
    [self filterContentForSearchText:searchString scope:[[self.searchDisplayController.searchBar scopeButtonTitles]
    objectAtIndex:[self.searchDisplayController.searchBar selectedScopeButtonIndex]]];
    return YES;
}

At this point, I haven't done any changes to the xib, no links and no other stuffs. If I compile I get my table, but obviously if I try to search something, nothing works. Moreover if I scroll down to the end of the table the app crashes. The real problem is that I can't see the search bar working. Could someone help me please?

Was it helpful?

Solution

Well you're on the right track... it is exactly to do with the connection of your controller class to your controller xib.

When you want to initialise a Search Bar and Search Display Controller into a UITableView, you are effectively adding a second table view that, when activated, must be managed by code in your UITableViewController class in the same manner as any UITableView.

I have used these SO questions/answers to check my own answer - I recommend you take a look:

I have read the Apple Documentation. I recommend you do the same to help you understand this.

First Step:

You will need to set data source and delegate methods for both table views when you run your controller class.

Before you do any of this, include this property...

@property (nonatomic, strong) UISearchDisplayController *searchController;

The following code describes how to initialise and set the appropriate properties for a UISearchBar and a UISearchDisplayController. If you are programmatically creating a UITableViewController in code you will also need to set the data source and delegate for it (not shown to keep the code easy to read).

You have two options here - which one you choose depends on your code and what you wish to achieve - either set these in your init/awakeFromNib methods, or set these in one of your table view controller (TVC) lifecycle methods.

Option One - Init

(Note1: Paul Hegarty's extraordinary iTunesU lectures taught me to init/awake a class as follows - in this way you are covered for both scenarios - you call init or it can awakeFromNib.)

- (void)setup {
    //  Provide initialisation code here!!!
    UISearchBar *searchBar = [[UISearchBar alloc] initWithFrame:CGRectZero];
    [searchBar sizeToFit];
    [searchBar setDelegate:self];

    [self setSearchController:[[UISearchDisplayController alloc] initWithSearchBar:searchBar
                                                                contentsController:self]];
    [self.searchController setSearchResultsDataSource:self];
    [self.searchController setSearchResultsDelegate:self];
    [self.searchController setDelegate:self];
}

- (void)awakeFromNib {
    [self setup];
}

- (id)initWithStyle:(UITableViewStyle)style {
    self = [super initWithStyle:style];
    if (self) {
        [self setup];
    }
    return self;
}

OR

Option Two - TVC Lifecycle

- (void)viewDidLoad {
    [super viewDidLoad];

    UISearchBar *searchBar = [[UISearchBar alloc] initWithFrame:CGRectZero];
    [searchBar sizeToFit];
    [searchBar setDelegate:self];

    [self setSearchController:[[UISearchDisplayController alloc] initWithSearchBar:searchBar
                                                                contentsController:self]];
    [self.searchController setSearchResultsDataSource:self];
    [self.searchController setSearchResultsDelegate:self];
    [self.searchController setDelegate:self];

    [self.tableView setTableHeaderView:self.searchController.searchBar]; // see Note2

    ...< other code as required >...
}

Note2: Regardless of which of these options you choose, you will need to place the following line of code in your viewDidLoad method...

    [self.tableView setTableHeaderView:self.searchController.searchBar]; // (or just searchBar)

Second Step:

Notes:

  • The table view that represents your complete data set (OldList) can be called using self.tableView (PS convention is to start each variable with lower case - so change your property name from OldList to oldList).

  • The table view that represents the filtered data set (filteredList) can be called using self.searchController.searchResultsTableView.

While you have prepared your tableView:cellForRowAtIndexPath: data source method, I suspect you have other data source (and maybe delegate) methods that need to be informed of which table view is the current table view, before they are able to function properly and provide you with a fully operational search results table view and search function.

For example:

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    if (tableView == self.searchController.searchResultsTableView)
        return 1;
    return [[self.oldList sections] count];;
}

and:

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    if (tableView == self.searchController.searchResultsTableView)
        return [self.filteredList count];
    return [self.oldList count];
}

Note that there may be other data source (and maybe delegate) methods that need to be informed of which table view is the current table view... I will leave it to you to determine which of these methods are to be modified, and the corresponding code necessary to adjust the table view.

Third Step:

You will be required to register a nib and reuse identifier for your search results table view.

I prefer to create a separate nib file (called "TableViewCellSearch.xib") that contains one table view cell, with the reuse identifier "SearchCell", and then place the code to register this nib and reuse identifier in the following UISearchDisplayController delegate method.

It is worth noting that this code is just as effective after the code block examples above in init/awakeFromNib/viewDidLoad.

- (void)searchDisplayController:(UISearchDisplayController *)controller willShowSearchResultsTableView:(UITableView *)tableView {
    static NSString *cellIdentifierSearch = @"SearchCell";
    UINib *nib = [UINib nibWithNibName:@"TableViewCellSearch" bundle:nil];
    [self.searchController.searchResultsTableView registerNib:nib forCellReuseIdentifier:cellIdentifierSearch];
}

Try these suggestions.

Hope this helps.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top