Mar 29

In this post I will be creating a Search screen, which will have a table view with a search bar. Table should display all the records if search field is empty other wise it should show all the records that have matching strings with the search field.

At the end, it should look like this:
For basics, refer my previous posts

So to start create a new ViewBased Project and name it as MySearchScreenApp. You can start with any template you want but you can find in my previous posts why I always start with the most basic template.
The Search screen that you can see in the above images are implemented in MySearchScreenAppViewController class. So open the header file MySearchScreenAppViewController.h and add the following code:

@interface MySearchScreenAppViewController : UIViewController {

UITableView *myTableView;

NSMutableArray *dataSource; //will be storing all the data

NSMutableArray *tableData;//will be storing data that will be displayed in table

NSMutableArray *searchedData;//will be storing data matching with the search string

UISearchBar *sBar;//search bar

}

@property(nonatomic,retain)NSMutableArray *dataSource;

@end

Here we have only defined the UIElements and some data sources. There are three mutable arrays. dataSource will be containing all the data and will be initialized before loading the searchScreen. searchedData will be holding a subset of dataSource based on search string. It will be contents of tableData which you will be viewing in the table below searchBar.
To implement all these elements, open MySearchScreenAppViewController.m file and add the following code in loadView:

- (void)loadView {

[super loadView];

sBar = [[UISearchBar alloc]initWithFrame:CGRectMake(0,0,320,30)];

sBar.delegate = self;

[self.view addSubview:sBar];

myTableView = [[UITableView alloc]initWithFrame:CGRectMake(0, 31, 300, 400)];

myTableView.delegate = self;

myTableView.dataSource = self;

[self.view addSubview:myTableView];

//initialize the two arrays; dataSource will be initialized and populated by appDelegate

searchedData = [[NSMutableArray alloc]init];

tableData = [[NSMutableArray alloc]init];

[tableData addObjectsFromArray:dataSource];//on launch it should display all the records

}


The only code worth explanation here is the last line [tableData addObjectsFromArray:dataSource]
As already discussed, dataSource will be holding all of data and tableData will be holding data to be shown in table. This code copies all elements of dataSource into tableData so that when the search screen is visible first time, all the data is displayed.
Now we implement all the tableViewDelegate methods: In the same file add the following code:

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {

return 1;

}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {

//NSLog(@”contacts error in num of row”);

return [tableData count];

}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

static NSString *MyIdentifier = @”MyIdentifier”;

UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];

if (cell == nil) {

cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:MyIdentifier] autorelease];

}

cell.text = [tableData objectAtIndex:indexPath.row];

return cell;

}

If you do not understand the code above, you should refer my earlier posts. These methods will be displaying data in tableData array into our table view.
Now we have to implement search bar delegate methods, which will be controlling the contents of tableData. In the same file add the following code:

#pragma mark UISearchBarDelegate

- (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar

{

// only show the status bar’s cancel button while in edit mode

sBar.showsCancelButton = YES;

sBar.autocorrectionType = UITextAutocorrectionTypeNo;

// flush the previous search content

[tableData removeAllObjects];

}

- (void)searchBarTextDidEndEditing:(UISearchBar *)searchBar

{

sBar.showsCancelButton = NO;

}


In the first method, we are only changing the appearance of search bar when a user taps on search bar. Refer the first two images to find the difference. The main code will be written now:

- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText

{

[tableData removeAllObjects];// remove all data that belongs to previous search

if([searchText isEqualToString:@""]searchText==nil){

[myTableView reloadData];

return;

}

NSInteger counter = 0;

for(NSString *name in dataSource)

{

NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];

NSRange r = [name rangeOfString:searchText];

if(r.location != NSNotFound)

{

if(r.location== 0)//that is we are checking only the start of the names.

{

[tableData addObject:name];

}

}

counter++;

[pool release];

}

[myTableView reloadData];

}

This method will be called whenever text in the search bar is changed. We should update tableData array accordingly. So the first line[tableData removeAllObjects]; removes all the searched records from previous search string.
Then we check if the search string is null so that we should return immediately with and empty tableData.
If the searchString is not null then we will go through each object in dataSource and select those objects which have the occurrence of search string in beginning. You can modify the code to have any kind of search though.
The last line[myTableView reloadData]; refreshes the table view. What ever be the content of tableData will be shown now.
We should also show all the records if the search string is cleared or cancelled. For that add the following code:

- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar

{

// if a valid search was entered but the user wanted to cancel, bring back the main list content

[tableData removeAllObjects];

[tableData addObjectsFromArray:dataSource];

@try{

[myTableView reloadData];

}

@catch(NSException *e){

}

[sBar resignFirstResponder];

sBar.text = @”";

}

// called when Search (in our case “Done”) button pressed

- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar

{

[searchBar resignFirstResponder];

}

In the first function, we have flushed all the searched records and simply copy all the records from dataaSource. In the second function we are telling the text pad to resign. You can skip this method to see what happens.
Thats it!! We are done with the search screen. But we have to initialize it from AppDelegate. So open MySearchScreenAppAppDelegate.m and add the following code:

- (void)applicationDidFinishLaunching:(UIApplication *)application {

// Override point for customization after app launch

viewController.dataSource = [[NSMutableArray alloc]init];

for(char c = ‘A’;c<=‘Z’;c++)

[viewController.dataSource addObject:[NSString stringWithFormat:@"%cTestString",c]];

UINavigationController *nvc = [[UINavigationController alloc]initWithRootViewController:viewController];

viewController.title = @”Search”;

[window addSubview:nvc.view];

[window makeKeyAndVisible];


Here we have only added dummy data into dataSource to test. You can always store data from database or anywhere .
Now build and run the application. Try typing characters in SearchField, you will find that the search is case sensitive. You will also notice that the search works only if you type the beginning characters only. If you want that the searched records should have search string occurance at any place, here is an alternative implementation of (void)searchBarTextDidEndEditing:(UISearchBar *)searchBar

- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText

{

[tableData removeAllObjects];// remove all data that belongs to previous search

if([searchText isEqualToString:@""]searchText==nil){

[myTableView reloadData];

return;

}

NSInteger counter = 0;

for(NSString *name in dataSource)

{

NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];

NSRange r = [name rangeOfString:searchText];

if(r.location != NSNotFound)

[tableData addObject:name];

counter++;

[pool release];

}

[myTableView reloadData];

}

I hope this post was useful. I will suggest you shou should read Predicates to optimize the search if you have very large data source to be searched.
Comments will be appreciated.

24 Responses to “iPhone Tutorial: Adding a search bar in TableView”

  1. skank3ymunky says:

    how would you search an xml array brought in using the NSXML parser?

  2. ABC says:

    Could you please provide a little more detail as in what you are trying to do?

  3. John says:

    This literally Fucked up my entire project! Now I have 22 errors and I don’t know why!

  4. ABC says:

    Look for a missing semicolon ..it sometimes triggers 500+ errors in my code ..lol
    any ways..look through your code, you must have done something wrong..

  5. Harinder says:

    Thanks a lot. It works but there are just few syntactic in it..

    I have made the few changes in it..

    - (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
    {
    [tableData removeAllObjects];// remove all data that belongs to previous search
    if([searchText isEqualToString:@""] || searchText==nil)
    {
    [myTableView reloadData];
    return;
    }
    NSInteger counter = 0;
    for(NSString *name in dataSource)
    {
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];
    NSRange r = [name rangeOfString:searchText];
    if(r.location != NSNotFound)
    {
    if(r.location== 0)//that is we are checking only the start of the names.
    {
    [tableData addObject:name];
    }
    }
    counter++;
    [pool release];
    }
    [myTableView reloadData];
    }

  6. ABC says:

    Thanks Harinder, can u plz let me know the place where u found d issue..i usually paste the code from my working code only.

  7. asdf says:

    i have tried several of these and i get 30-40 errors each time
    i have no idea what i am doing wrong
    i follow it step by step

  8. ABC says:

    paste your code here so that we can debug it,,

  9. iPhoneDev says:

    Nice tutorial, however when I build and run the app it quits due to an uncaught exception. I have several warnings in the code that I’m not sure what they mean:

    http://img18.imageshack.us/img18/5504/warningsc.jpg

    Do you know what’s causing this and how to fix them? Thanks.

  10. AnotherDev says:

    Hi,

    Seems very useful. I’m running xCode version 3.1.3 and I am receiving close to 30 compilation errors.

    loadView compiled full of errors. Could you supply us with your working xCode project as it appears that a few people who posted here are having compile problems.

  11. Weapon says:

    @ iPhoneDev: Yes you have to write a piece of code like this in your header file

    @interface DataSet : UITableViewController {
    //rest of the code goes here
    }

  12. Weapon says:

    And implement the UITableViewDelegate, UITableViewDataSource, UISearchBarDelegate classes

  13. philip says:

    i have the same warning messages as iPhoneDev encountered above, and the app quite unexpectedly in simulator.

  14. ABC says:

    I realized that my code examples have some compatibility issues with 3.0, will fix em soon and upload the source files

  15. Maulik says:

    Hi ABC,
    You havent used searchedData at all. I guess u should be using searchedData instead of tableData array in the Search Delegate.

  16. Eddie says:

    I have some issues with your tutorials as well. I’m running Xcode 3.2.1. Maybe you’re right about the compatibility.

  17. sbs says:

    the code is working but i am not getting the list of contents wwhen i clear the search bar. And also error occurs coz of redefinition of
    - (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText

  18. sbs says:

    how to make search case insensitive?

  19. Kervich says:

    Obviously, the view controller should implement several delegate protocols for the things to work, so the declaration should look like this:

    @interface MySearchScreenAppViewController : UIViewController

  20. Kervich says:

    Obviously, the view controller should implement several delegate protocols for the things to work, so the declaration should look like this:

    @interface MySearchScreenAppViewController : UIViewController < UISearchBarDelegate, UITableViewDelegate, UITableViewDataSource >

  21. app says:

    Hello everybody, can somebody send this code in xCode files? Because it’s a lot of mistakes. Maybe i don’t understand something. I very need this example. Thanks a lot.

  22. Jason says:

    ABC thanks for the code. Works great.

    You do have a typo though on this line:

    if([searchText isEqualToString:@""]searchText==nil)

    It should be this:

    if([searchText isEqualToString:@""] || searchText==nil)

    Also, searchedData is never used so can be removed.

    Thanks!

    Seems to be causing a lot of people confusion.

  23. Ambert says:

    Thanks for the code ABC. Kudos on being patient with the people who commented!

Leave a Reply

preload preload preload