RSS

Core Data Basics Part 7 – Search Bars

05 Mar


Introduction

If you’ve used an iPhone or iPad before you’ve probably used a Search Bar. This tutorial will show how to add a Search Bar to your project.  Again we will be using the Staff Manager project from previous parts of this tutorial set.  Specifically we will be adding the Search Bar to the Roles Table View of the Staff Manager project. Here’s what we’re aiming at:

Prerequisites

We follow on from Part 6, so download the project from the end of Part 6 then extract and open it with Xcode.

Adding a Search Bar

Select MainStoryboard.storyboard and find the Roles Table View Controller on the canvas. Drag a Search Bar and Search Display Controller on to the Roles Table View.  Be careful not to drag it into the prototype cell.

The Roles Table View should now look like this with a Search Bar:

Run the app and go to the Roles tab. Start typing in the Search Bar and you will notice the table view does not filter out data which doesn’t match the search criteria. Useless!

Making the Search Bar Useful

The data in the Table View should be filtered to show search results as a user types in the Search Bar. In order for this to work we need to set up the RolesTVC class to implement the UISearchDisplayDelegate and UISearchBarDelegate protocols. Update the following line in RolesTVC.h:

@interface RolesTVC : CoreDataTableViewController <AddRoleTVCDelegate, RoleDetailTVCDelegate, UISearchDisplayDelegate, UISearchBarDelegate>

 

To display a filtered subset of data we need to store the search results in an array. What better place than in a  searchResults NSMutableArray!  Add the following property to RolesTVC.h:

@property (nonatomic, retain) NSMutableArray *searchResults;

 

Of course, you will then need to add the following to RolesTVC.m:

@synthesize searchResults;

 

Also, create the following methods in RolesTVC.m.  They are responsible for initial setup and removal of the searchResults array:

- (void)viewDidLoad 
{
    self.searchResults = [NSMutableArray arrayWithCapacity:[[self.fetchedResultsController fetchedObjects] count]]; 
    [self.tableView reloadData];
}
 
- (void)viewDidUnload
{
	self.searchResults = nil;
}

 

So far in the tutorial series the CoreDataTableViewController has shielded us from needing to configure the numberOfRowsInSection method required by a TableView. This method simply tells the table how many rows to display.  The answer will be different depending on whether we are showing all data or only searchResults.  Add the following method to RolesTVC.m:

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
	if (tableView == self.searchDisplayController.searchResultsTableView)
	{
        return [self.searchResults count];
    }
	else
	{
        return [[[self.fetchedResultsController sections] objectAtIndex:section] numberOfObjects];
    }
}

 

Likewise, the existing cellForRowAtIndexPath method also needs to differentiate between data from the searchResults and otherwise.  Change the cell configuration in the cellForRowAtIndexPath method within RolesTVC.m as follows:

    // Configure the cell...
    Role *role = nil;
 
	if (tableView == self.searchDisplayController.searchResultsTableView)
	{
        NSLog(@"Configuring cell to show search results");
        role = [self.searchResults objectAtIndex:indexPath.row];
    }
	else
	{
        NSLog(@"Configuring cell to show normal data");
        role = [self.fetchedResultsController objectAtIndexPath:indexPath];
    }

 

The next method is where most of the search filter magic happens. When you adapt the code to your own needs you will need to know what is happening here.  Here’s the overview:

  1. All searchResults are removed.
  2. A role object is created for every object fetched from Core Data.
  3. A role object is put into the searchResults array for all roles with a role name that matches searchText.

Add the following method to RolesTVC.m:

#pragma mark -
#pragma mark Content Filtering
 
- (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope
{
	NSLog(@"Previous Search Results were removed.");
	[self.searchResults removeAllObjects]; 
 
	for (Role *role in [self.fetchedResultsController fetchedObjects])
	{
		if ([scope isEqualToString:@"All"] || [role.name isEqualToString:scope])
		{
			NSComparisonResult result = [role.name compare:searchText 
                                                   options:(NSCaseInsensitiveSearch|NSDiacriticInsensitiveSearch) 
                                                     range:NSMakeRange(0, [searchText length])];
            if (result == NSOrderedSame)
			{
                NSLog(@"Adding role.name '%@' to searchResults as it begins with search text '%@'", role.name, searchText);
				[self.searchResults addObject:role];
            }
		}
	}
}

Finally add these two additional small methods to RolesTVC.m:

#pragma mark -
#pragma mark UISearchDisplayController Delegate Methods
 
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString
{
    [self filterContentForSearchText:searchString scope:@"All"];
    return YES;
}
 
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchScope:(NSInteger)searchOption
{
    [self filterContentForSearchText:[self.searchDisplayController.searchBar text] scope:@"All"];
    return YES;
}

Note that I haven’t shown you how to configure search scopes or anything fancy. I didn’t want to show too much at once. If you’re comfortable with the basics, check out Apple’s TableSearch project to learn more.

Run the app now and you should be able to filter the Roles using the Search Bar:

_______

That’s it!

Here’s the complete source code so far

If you liked this tutorial or found something wrong with it please let me know!

If you want to support my work and have an iPad please consider purchasing iSoccer *wink*

-Tim

Go to Part 8 or the Tutorials Index


Be Sociable, Share!
     

    About Tim Roadley

    I'm a senior analytics software consultant at eMite Pty Ltd primarily focused on delivering business intelligence dashboards, currently for one of Australia’s major banks. I'm also working on a revamped version of eMite's iOS App for release under iOS 7. Prior to eMite, I was Infrastructure Manager at Cuscal Pty Ltd where I was heavily involved in designing and implementing a payments switch that drives 1300+ ATM’s Australia wide. I have several apps on the App Store, including Teamwork, iSoccer and now Grocery Dude and Grocery Cloud. In my down time I enjoy spending time with my wonderful wife Tracey and two lovely children Tyler and Taliah.
    52 Comments

    Posted by on March 5, 2012 in iOS Tutorials

     

    52 Responses to Core Data Basics Part 7 – Search Bars

    1. Abhishek Sonawane

      March 5, 2012 at 9:16 pm

      Awesome tutorial Tim!!

      Amazing and super easy!!

      Cheers,
      Abhishek.

       
      • John Hebebrand

        March 4, 2013 at 4:16 am

        Although most all of these Core Data tutorials are spot on, the search code does not function as described. Seong got me most of the way there…selecting and segueing to the detail object. However when multiple items are returned with the search it always segues to the first item in the list. Has anyone figured out the search example and can you post the code? Thanks John

         
        • Graham Gardiner

          April 6, 2013 at 5:12 pm

          Hi john Hebebrand Did you work out what was to be done in the code to sort the problem of sending the top or first item on the search list? I have been trying to work it out. Please let me know if you have worked this problem out. Tim thank you for your great tutorials.

           
    2. Adrian

      March 6, 2012 at 1:12 pm

      Hi Tim
      Great tutorial pal. Very useful man. I see you changed the mobile theme on your web site. Pretty cool, pretty cool indeed. I like the way you use the pre loaded data in core data example bro. There are so many tuts in core data in ios 5 out there but are mostly generic stuff right out of apple docs , yours has a nice and easy approach. I hope everyone else enjoy following them as I do. Keep up the good work and be blessed.

      Adrian

       
    3. Maksanzhi

      March 6, 2012 at 8:46 pm

      Hi Tim,

      Great tutorial!
      I am a beginner in ios,
      can you make next tutorial: how to add to Favorites?

      Thank you

       
      • Tim Roadley

        March 7, 2012 at 8:19 pm

        Add favourites? like in safari? no idea!!

         
    4. Maksanzhi

      March 11, 2012 at 6:56 am

      I mean bookmarks ) sorry my english is very bad

       
      • Tim Roadley

        March 11, 2012 at 8:22 am

        I’m still unsure what you’re trying to do. You want to add an item from a tableview in to your safari bookmarks? I don’t think that’s possible? Do you have examples from google searches of what you want to do?

         
    5. Christina

      March 11, 2012 at 9:38 am

      Hi Tim,

      Tutorial looks to be JUST what I need for an app I have been struggling with in core data. Thanks, fellow Aussie!

       
      • Tim Roadley

        March 11, 2012 at 8:49 pm

        Glad you find them useful, good luck with you app!

        -Tim

         
        • shibi

          December 27, 2012 at 5:37 pm

          thanks

           
    6. santiago

      March 21, 2012 at 10:39 pm

      Hi Tim,

      great Job. Many thanks. I have one doubt. You see that the table view cells with the results are diferent to your custom previous ones. Could they have the same format and funcionality (go to modify the role)?

      Santi

       
    7. Seong

      March 23, 2012 at 4:26 pm

      Hello Tim, I am a fan of yours.
      I succeeded this searchbar work easily by ur kind help.
      But when I touch a cell filtered, the segue to the detailview does not perform. Its untouchable.
      Have I missed something?
      Thank you again anyway.

       
    8. Seong

      March 25, 2012 at 7:15 pm

      I ve got to know, the tableview that searchdisplaycontroller ceated is a new vanilla one so it doesnt hear anything other than just displaying data. So I ve tried to tell him to work ”didselectrow -> perfom the segue -> and show detailTVC with searchedresult’ . I should have told which data to show in preparing segue, so I tried to say if(seached), show searchedresult, else just show selected index row. But I ve failed to say ‘if(seached)’ condition. Maybe I m lost. Thank you as always.

       
    9. Seong

      March 27, 2012 at 2:12 am

      At last! I found the way. First, I wanna say Thank you so much, Tim. I ve grown up by your tutorials.
      This is how I succeed:
      at cellForRowAtIndexPath, UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:CellIdentifier]; i.s. self. is the point. It makes the tableviewcells those searchdisplaycontroller made to look and to work just like the original tableviewcells that I made. Of course Segue is performed too.
      And when prepareForSegue,
      if (searchResults)
      self.selectedDbPx = [self.searchResults objectAtIndex:indexPath.row];

      else
      self.selectedDbPx = [self.fetchedResultsController objectAtIndexPath:indexPath];

      shows the exact detail view for the searched result cells.
      Thata’s all that I needed!
      It’s been so hard 3 days for me thou.
      Than you again, Tim.

       
      • Charlie B

        December 3, 2013 at 10:39 am

        Nice one Seong, your 3 days saved me 5 days!!

         
    10. Seong

      March 27, 2012 at 3:11 am

      Sorry but the solution above I wrote missed something. not works properly. More investigation is needed.

       
      • santiago

        March 27, 2012 at 4:58 am

        Hi Seong,
        I have tried many things and the only solution I have found is this:
        cell = [[[NSBundle mainBundle] loadNibNamed:@”CCell” owner:self options:nil] objectAtIndex:0];
        Although I am using StoryBoard I created a xib file to define the cell. In the tablevew I don’t define any prototipe cell.
        The cell is never null …
        I hope It is useful for you.

         
        • Freitas

          October 22, 2012 at 3:46 am

          Hello Tim,
          I confess I’m also a bit lost in this situation.

          someone already has a simple solution.

           
    11. Tim Roadley

      March 27, 2012 at 5:41 am

      Seong,

      Sorry I’ve not replied in a while, I have been so busy at work recently I haven’t had time! A quick search led me to this question on stack overflow. Have you tried that?

      When I get a change I will look in to this issue more

      Cheers

       
    12. Seong

      March 27, 2012 at 1:02 pm

      Thank you Tim and santiago.
      As always, Tim saved me.
      //santiago ‘s help was very useful too:)

      Finally it works, maybe.
      using a hint used in this tutorial just what Tim suggested me.

      at cellForRowAtIndexPath,
      UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:CellIdentifier];

      is still doing great.

      After that, like the tutorial, set a property of an NSString *savedSearchTerm. and @synthsize savedSearchTerm;.

      and insert a line at,

      - (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope
      {
      [self setSavedSearchTerm:searchText];
      ......

      And the last one thing,

      - (void)searchDisplayControllerWillEndSearch:(UISearchDisplayController *)controller
      {
      [self setSavedSearchTerm:nil];
      }

      and that’s all.

      Now

      if (savedSearchTerm){

      works, in preparing for Segue.

      the savedSearchTerm does not save any search term though. :)
      it looks not so beautiful, but it works yet :)

      Thank you.

       
      • Zachary Fisher

        May 31, 2012 at 7:47 am

        Seong,
        Thanks for helping figure this out. It has been giving me a headache for days… one question. When I use your code, it only really works if there is a single object in the search results. If, for example, I type in the character “c”, both c++ and chief financial officer are choices. No matter which one I select, it goes to the c++ detail. I am assuming this is because the indexpath.row info is not being passed in the searched table, so it can’t grab the second item. Did you notice this? Any thoughts on how to fix?

        Thanks!
        Zack

         
    13. charlesGray

      April 4, 2012 at 10:21 am

      very helpful tutorials … i like your ‘style’ of instruction … thanks

      this tutorial would be much more helpful IF after the selection process you could actually ‘select’ one of the “cellsForRowAtIndexPath” and seque to the ‘edit’ tableView.

      what’s next?

       
      • Tim Roadley

        April 4, 2012 at 10:29 am

        I understand the issue completely and am working on a solution. I’ll let you know when I have one.

        Cheers

         
        • charlesGray

          April 4, 2012 at 10:51 am

          thanks for your timely reply and understanding ….

          i’ll let you know if i craft a solution, before hearing from you

           
        • Chrys

          May 25, 2012 at 12:53 pm

          Any update on this? Not sure the best way to segue from a searched cell? …

           
        • Chrys

          May 25, 2012 at 1:04 pm

          Never mind, I posted without reading properly – Seong’s solution is awesome

           
    14. charlesGray

      April 4, 2012 at 10:22 am

      follow-up requested

       
      • charlesGray

        April 6, 2012 at 2:20 am

        i have a continuing issue with adding the managedObjectContext obj setup in the appDelegate …

        saw the same compilation error the first time i wrote your tutorial … but it ‘went away’ … unfortunately don’t know what i did to correct the problem

        code is all correct, but i get a “Receiver type … forward declaration” error …

        what causes this? and how do you make it happy ?

         
        • charlesGray

          April 6, 2012 at 3:30 am

          found the answer … F.Y.I.

          by including the Stanford CoreDataTableViewController you get the import of CoreData/CoreData.h … until that point the ‘… forward declaration’ error is stubbornly present

          http://stackoverflow.com/questions/8088030/cannot-create-a-managed-object-context-on-ios

          thanks again for all you’re doing to further the education process for so many!

           
        • Tim Roadley

          April 6, 2012 at 6:35 am

          Great news that you found the fix so quickly, gotta love Stack Overflow :)

           
    15. Tony Short

      May 22, 2012 at 6:26 am

      I have followed this tutorial and have found it most useful. My question to Tim or to his followers is this:

      Rather than fetching from a search bar, could I have a Text Field and a button that allows the user to enter a search criteria followed by clicking submit to search?

      The app that I’m aiming to create will have pre-loaded data and I need a fetch mechanism to allow the user to search on the data.

      Thank You

      Tony

       
    16. zav

      June 23, 2012 at 4:28 am

      Great, but what do we do now if we want to move the search bar out of the tableView and into its own view so it doesn’t scroll away?

      It’s not exactly smooth sailing in the storyboard or in restructuring the code.

      Any advice would be appreciated.

       
      • Jack

        August 11, 2012 at 11:53 pm

        @zav thats what I’d like to know

         
    17. Reno Chong

      June 26, 2012 at 6:31 pm

      Hey, I would like to ask how you configure the cancel function on the search bar but when i look at the Roles.m file, I could not find any cancel method for the search bar. Hope to hear from you soon.

      Thank you.

       
    18. Cuong Duong

      June 29, 2012 at 3:10 pm

      Hi Tim,
      Thank you for your examples.
      I’m a newer for Objective-C programming, your examples is very helpful for me.
      I have a question for part 7, Why I can’t tab on search result for view detail? How do I for view detail when tab on seach result?
      Thanks so much.

       
    19. Cuong Duong

      June 29, 2012 at 3:11 pm

      Hi Tim,
      Thank you for your examples.
      I’m a newer for Objective-C programming, your examples is very helpful for me.
      I have a question for part 7, Why I can’t tab on search result for view detail? How do I for view detail when tab on search result?
      Thanks so much.

       
    20. Johny Mnemonic

      October 4, 2012 at 6:20 am

      Tim, let the power of Universe be with you ! ! !
      Cheers from Ukrainian developer !

       
    21. Leandro Davi de Freitas

      October 22, 2012 at 5:37 am

      Hello Cuong Duong and Seong,

      for those who are still having the problem.
      I got the solution I found was the following:

      – (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
      {
      if (tableView == self.searchDisplayController.searchResultsTableView)
      {

      RoleDetailTVC *nv = [self.storyboard instantiateViewControllerWithIdentifier:@"identifierName"];//click on the storyboard view and put that name as an identifier

      nv.delegate = self;
      nv.managedObjectContext = self.managedObjectContext;
      self.selectedRole = [self.searchResults objectAtIndex:indexPath.row];
      nv.role = self.selectedRole;
      [self.navigationController pushViewController:nv animated:YES];

      }

      }

      I have helped.

       
    22. JP

      January 12, 2013 at 4:20 am

      Hi Leandro,

      That doesn’t seem to work for me. It opens it up but doesn’t display the selected role, also it adds to the stack somehow so when clicking the back button you have to do it twice.

      Hi Tim,
      I’ve downloaded the completed code after section 8, and in the completed code the search doesn’t work either since from a search you can’t actually go to the detail. Has anyone found the suggested solution?

      Thanks

       
    23. Chen Lala

      January 13, 2013 at 11:32 am

      Hi Tim if I use sql to be database , how do I to connect with XXX.jpg file ? My purpose is to write [Hotel App] so I need to show hotel photo. Do you have any suggestion about framework! thanks for your respond.

       
    24. Kavya

      September 23, 2013 at 5:40 pm

      Hi Time,
      particulary for the above tutorial, I want to know how to do searching with core data. After searching i want to edit filtered data and update into my core data. Above tutorial has Roles statically populated in appdelegate class. In my app, I’ve core data to show in tableview which also has search bar on top. I am able to search using UISearchDisplayController. I can also see the filtered result according to given predicate. But when I edit/update that data I see an inconsistancy in core data. Can you please add search bar on your Person details table and show edit/update on it?

       
    25. Kavya

      September 23, 2013 at 6:17 pm

      Hi Tim,
      also I have notice that on Role tab, if you serach for a record and after that delete that cell (search is still on) then it results in deleting indexpath.row from the original table

       
      • Tim Roadley

        September 23, 2013 at 9:53 pm

        These tutorials are a little old now and I’ve been pouring all of my effort into finalising the “Learning Core Data for iOS” book so it can be released very soon! Search is fully covered in the book and implemented in a much neater way :)

         
    26. mvasco

      November 23, 2013 at 4:13 pm

      Hi, i have followed your tutorial and found it very interesting, thank you Tim. I have also requested your book here in Barnes & Noble in El Paso,TX. I have also read all the comments but I still have a problem related to the segue from a searched cell to the detail view. I would appreciate if someone could help me. The proposals from other users before are not working for me. Thank you….

       
      • Graham Gardiner

        November 23, 2013 at 11:02 pm

        Hi Mvasco I had the same problem but worked it out the trick is in the following if you are still stuck email me @ safarigrabber@yahoo.co.uk and I can look at what code you have already.

        – (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
        {

        if (savedSearchTerm) {

        searchRowSelected = indexPath.row; //<– the trick that worked

        }

        }

        Good luck
        Regrards
        Graham

         
        • Modesto Vasco

          November 24, 2013 at 3:22 am

          Thank you Graham, you have been very nice to me. In the meantime I have solved the issue as follows, in my case I wanted to implement the search function on to the PersonsTVC, but the solution is of course valid to the RolesTVC: 1. I have included the same method as you propose, but with different content:
          – (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
          {

          // Perform segue to detail when a SEARCH table cell is touched
          if(tableView == self.searchDisplayController.searchResultsTableView)
          {
          [self performSegueWithIdentifier:@"Person Detail Segue" sender:tableView];
          }

          }

          And then I have updated the prepareForSegue method as follows:

          – (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
          {
          if ([segue.identifier isEqualToString:@"Add Person Segue"])
          {
          NSLog(@”Setting PersonsTVC as a delegate of PersonDetailTVC”);
          PersonDetailTVC *personDetailTVC = segue.destinationViewController;
          personDetailTVC.delegate = self;

          NSLog(@”Creating a new person and passing it to PersonDetailTVC”);
          Person *newPerson = [NSEntityDescription insertNewObjectForEntityForName:@"Person"
          inManagedObjectContext:self.managedObjectContext];

          personDetailTVC.person = newPerson;
          }
          else if ([segue.identifier isEqualToString:@"Person Detail Segue"])
          {
          NSLog(@”Setting PersonsTVC as a delegate of PersonDetailTVC”);
          PersonDetailTVC *personDetailTVC = segue.destinationViewController;
          personDetailTVC.delegate = self;

          // Store selected Person in selectedPerson property
          if(sender == self.searchDisplayController.searchResultsTableView)
          {
          NSIndexPath *indexPath = [self.searchDisplayController.searchResultsTableView indexPathForSelectedRow];
          self.selectedPerson = [self.searchResults objectAtIndex:[indexPath row]];
          }
          else
          {
          NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
          self.selectedPerson = [self.fetchedResultsController objectAtIndexPath:indexPath];
          }

          NSLog(@”Passing selected person (%@) to PersonDetailTVC”, self.selectedPerson.firstname);
          personDetailTVC.person = self.selectedPerson;
          }
          else
          {
          NSLog(@”Unidentified Segue Attempted!”);
          }
          }

          Now it is working as expected, the only issue is that after updating or saving the info on the detail view, the app returns to the search results view….

          THank you again, I hope it will help.

           
    27. jacob banks

      December 2, 2013 at 8:30 am

      Hi Tim this is a great tutorial thank you, I’m working on my first app and i have a choose subject cell kind of like your choose role, and when clicked on it , it goes to a second view with subjects that they added and then when you click on one of those it will cancel out of the view and i need it to add the class they chose to the choose subject cell title. I know you show how to do it in this tutorial but i can’t figure out how to get it into my app because i don’t have a lot of the things that you do

       
    28. paddy211185

      June 21, 2014 at 12:11 pm

      Hi @ all, have everybodey found a solution? Have use the Solution from Modesto Vasco

      November 24, 2013 at 3:22 am , but it crash on the iPad

       

    Leave a Reply