1

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?

rmaddy
  • 314,917
  • 42
  • 532
  • 579
TheInterestedOne
  • 748
  • 3
  • 12
  • 33
  • 1
    There are a number of elements missing from your code. My suggestion is to run a test project using Storyboard, drag a `UISearchDisplayController` into a `UITableView`, and observe the automatic outlet connections that are made by Xcode. These will include setting the appropriate delegates. You'll also need to address how your table view data source methods prepare the `searchResultsTableView`. You'll find many good tutorials from a Google search. There are many questions on SO that address the issue you present. If you are still stuck after this, let me know and I'll offer more suggestions. – andrewbuilder May 02 '14 at 01:05
  • @andrewbuilder First of all thank you for your answer. I've searched for a solution on google and I've found some interesting tutiorials, that's true, but I had not found a way yo solve my problem (my bad for sure). I'll try to go deep in my code and I'll try to run a test project with storyboard, as you have suggested – TheInterestedOne May 02 '14 at 08:33
  • @andrewbuilder I've followed your suggestion, but I'm stucked here. I think the main problem is linked to the connection from the code to the xib. – TheInterestedOne May 03 '14 at 10:51
  • This might help someone http://stackoverflow.com/a/39388169/2033377 – Deepak Thakur Sep 08 '16 at 10:40

1 Answers1

19

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.

Community
  • 1
  • 1
andrewbuilder
  • 3,629
  • 2
  • 24
  • 46
  • thank you so much for your time and your help. I've found this answer so useful and complete. This just for say that I really appreciate your work and your time spent to help me! Thanks! – TheInterestedOne May 05 '14 at 18:18
  • @andrewbuilder ...thank you for your help but i have little problem...i am using tableviewcontroller by story board with pull to refresh functionality and also implement searchview display controller and its working fine but i want this searchdisplay controller always shown on top of the tableview...i tried much but still not success....can you please help me how can i achieve this?? – Bandish Dave Apr 23 '15 at 05:04
  • Thanks @BandishDave while I'm happy to try to help, I believe your problem is basis for a new question. I'd recommend you detail your problem in a new question. If you do that, let me know and I'll have a look. – andrewbuilder Apr 26 '15 at 04:12
  • @andrewbuilder :: thank you for replay... here is my question....http://stackoverflow.com/questions/29821860/search-view-shown-always-top-on-tableview?noredirect=1#comment47775822_29821860...... please let me know if you have any solution ... – Bandish Dave Apr 27 '15 at 05:38
  • @BandishDave seems SKT provided a suitable answer to your question, a good outcome. Let me know if you get stuck again. – andrewbuilder Apr 27 '15 at 07:31
  • @andrewbuilder : yes you are right that skt gave an answer but he suggested searchview with UIViewController while i want in UITableviewController....so is that possible?? and if yes than how? – Bandish Dave Apr 27 '15 at 11:59
  • @BandishDave there are ways and I've done it before, by placing a `UIView` in your storyboard hierarchy at the same level as the `UITableView`. I'll review what is involved and respond to your question. – andrewbuilder Apr 27 '15 at 21:13