Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

iOS

How to implement search function in Ribbit app?

Hi All,

I have created the Ribbit app, following Ben's helpful tutorial. And I had a few ideas to add to it.

Instead of users having to scroll through everyone who is on my apps database, just to find one person, I would like to have a search feature. This would mean, upon tapping 'edit' on the friends screen, the user could then tap in a search bar, and find the person they are looking for. No users would appear before the search, everyone else who is listed on the database would appear invisible (if that makes sense?) So, only the user that has that name, would appear in their search results, not the whole database.

How would i go about implementing this?

Cheers, Ryan

16 Answers

Stone Preston
Stone Preston
42,016 Points

in your add friends view controller, you would need to drag a search bar onto your view in the header portion of the table view. Make sure you set that the bar has a cancel button in the attributes inspector. Then in your header file you would have to conform to the UISearchBarDelegateProtocol

: UITableViewController <UISearchBarDelegate>

You also need to add that search bar you dragged onto the view as a property as well as a PFObject property to hold the user that is found.

@property (weak, nonatomic) IBOutlet UISearchBar *searchBar;
@property (strong, nonatomic) PFObject *user;

set the search bar delegate in view did load

self.searchBar.delegate = self;

and implement these methods

- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar {

    //dismiss keyboard 
    [self.searchBar resignFirstResponder];

}

- (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar {

    //Enable the cancel button when the user touches the search field
    self.searchBar.showsCancelButton = TRUE;
}

- (void)searchBarTextDidEndEditing:(UISearchBar *)searchBar {

    //dsiable the cancel button when the user ends editing
    self.searchBar.showsCancelButton = FALSE;
}

- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar {

    //dismiss keyboard
    [self.searchBar resignFirstResponder];


    //Strip the whitespace off the end of the search text
    NSString *searchText = [self.searchBar.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];


    //Check to make sure the field isnt empty and Query parse for username in the text field
    if (![searchText isEqualToString:@""]) {

        PFQuery *query = [PFUser query];
        [query whereKey:@"username" equalTo:searchText];
        [query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
            if (!error) {

                //check to make sure the query actually found a user
                if (objects.count > 0) {

                    //found a user
                  self.user = [objects objectAtIndex:0];


                //A user was not found, display error message
                } else {

                    //no user found

                }

                [self.tableView reloadData];

            } else {

              //error occurred
            }
        }];

    }


}

you would also need to make sure your tableView delegate methods use the same self.user property you set in the query block (as opposed to self.allUsers)

As far as making the table view empty when the view appears just set the data source to nil in viewWillAppear. That will clear out the data anytime the view appears so you get a blank view when it appears, when you search and find a user the tableview shows that user, and when it appears again its blank again.

Thank you!

Hi I'm adding this to the editViewController. But I get an error on: - (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar {

Any good tips what Im doing wrong?

Stone Preston
Stone Preston
42,016 Points

what error did you get?

Thanks for looking in to it. Helping a noob out:)

Ok. So I don't remove anything from the editViewController that was made with ribbit. I just add your code to the .h and .m file. Maybe I'm doing it wrong?

See errors below.

1) Error:

  • (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar {

Issue: Use of undeclared identifier 'searchBarCancelButton Clicked

2) Error: PFQuery *query = [PFUser query]; [query orderByAscending:@"username"]; [query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) { if (error) { NSLog(@"Error: %@ %@", error, [error userInfo]); } else { self.allUsers = objects; [self.tableView reloadData]; } }];

Issue:

  • Use of undeclared identifier 'query'
  • Use of undeclared identifier 'query'

This issue is marked over: [query orderByAscending:@"username"]; [query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {

Stone Preston
Stone Preston
42,016 Points

you didnt conform to the UISearchBar delegate protocol

: UITableViewController <UISearchBarDelegate>

How do I do that:) I added it in header file. Should I do it in the .m file too?

Stone Preston
Stone Preston
42,016 Points

just the header. paste the code you used to do that. you may have used searchBarControllerDelegate instead of searchBarDelegate

The full code .h file:

import <UIKit/UIKit.h>

import <Parse/Parse.h>

@interface EditFavoritesViewController : UITableViewController <UISearchBarDelegate> @property (weak, nonatomic) IBOutlet UISearchBar *searchBar;

@property (nonatomic, strong) NSArray *allUsers; @property (nonatomic, strong) PFUser *currentUser; @property (nonatomic, strong) NSMutableArray *friends;

  • (BOOL)isFriend:(PFUser *)user;

@end


The full code .m file:

import "EditFavoritesViewController.h"

@interface EditFavoritesViewController ()

@end

@implementation EditFavoritesViewController

  • (void)viewDidLoad { [super viewDidLoad];

    self.searchBar.delegate = self;

    • (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar {

      //dismiss keyboard [self.searchBar resignFirstResponder];

    }

    • (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar {

      //Enable the cancel button when the user touches the search field self.searchBar.showsCancelButton = TRUE; }

    • (void)searchBarTextDidEndEditing:(UISearchBar *)searchBar {

      //dsiable the cancel button when the user ends editing self.searchBar.showsCancelButton = FALSE; }

    • (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar {

      //dismiss keyboard [self.searchBar resignFirstResponder];

      //Strip the whitespace off the end of the search text NSString *searchText = [self.searchBar.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];

      //Check to make sure the field isnt empty and Query parse for username in the text field if (![searchText isEqualToString:@""]) {

      PFQuery *query = [PFUser query];
      [query whereKey:@"username" equalTo:searchText];
      [query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
          if (!error) {
      
              //check to make sure the query actually found a user
              if (objects.count > 0) {
      
                  //found a user
                  //set the user as the table views data source and reload the table view
      
                  //A user was not found, display error message
              } else {
      
                  //no user found
      
              }
      
              [self.tableView reloadData];
      
          } else {
      
              //error occurred
          }
      }];
      

      }

    }

    PFQuery *query = [PFUser query]; [query orderByAscending:@"username"]; [query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) { if (error) { NSLog(@"Error: %@ %@", error, [error userInfo]); } else { self.allUsers = objects; [self.tableView reloadData]; } }];

    self.currentUser = [PFUser currentUser]; }

pragma mark - Table view data source

  • (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { // Return the number of sections. return 1; }

  • (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { // Return the number of rows in the section. return [self.allUsers count]; }

  • (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *CellIdentifier = @"Cell"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];

    PFUser *user = [self.allUsers objectAtIndex:indexPath.row]; cell.textLabel.text = user.username;

    if ([self isFriend:user]) { cell.accessoryType = UITableViewCellAccessoryCheckmark; } else { cell.accessoryType = UITableViewCellAccessoryNone; }

    return cell; }

pragma mark - Table view delegate

  • (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { [self.tableView deselectRowAtIndexPath:indexPath animated:NO];

    UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];

    PFRelation *friendsRelation = [self.currentUser relationforKey:@"friendsRelation"]; PFUser *user = [self.allUsers objectAtIndex:indexPath.row];

    if ([self isFriend:user]) { cell.accessoryType = UITableViewCellAccessoryNone;

    for(PFUser *friend in self.friends) {
        if ([friend.objectId isEqualToString:user.objectId]) {
            [self.friends removeObject:friend];
            break;
        }
    }
    
    [friendsRelation removeObject:user];
    

    } else { cell.accessoryType = UITableViewCellAccessoryCheckmark; [self.friends addObject:user]; [friendsRelation addObject:user]; }

    [self.currentUser saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) { if (error) { NSLog(@"Error: %@ %@", error, [error userInfo]); } }]; }

pragma mark - Helper methods

  • (BOOL)isFriend:(PFUser *)user { for(PFUser *friend in self.friends) { if ([friend.objectId isEqualToString:user.objectId]) { return YES; } }

    return NO; }

@end

Ups. Don't know if that makes sense. On import it should say Parse/Parse.h and UIKit/UIKit.h in .h file

Stone Preston
Stone Preston
42,016 Points

you will need to format that using markdown. some of your code is missing since its not marked down

Sorry but how do i do that :(

Stone Preston
Stone Preston
42,016 Points

at the bottom right of the comment textbox there is a link that says Markdown Cheatsheet

Thanks:) Ok. Does it matter what type of search bar you drag in to the .h file as a property?

// EditFavoritesViewController.h

            <p>#import <UIKit/UIKit.h>
#import <Parse/Parse.h>

@interface EditFavoritesViewController : UITableViewController <UISearchBarDelegate>
@property (weak, nonatomic) IBOutlet UISearchBar *searchBar;

@property (nonatomic, strong) NSArray *allUsers;
@property (nonatomic, strong) PFUser *currentUser;
@property (nonatomic, strong) NSMutableArray *friends;

- (BOOL)isFriend:(PFUser *)user;

@end
</p>
            ``` 


//  EditFavoritesViewController.m
  ```html
            <p>#import "EditFavoritesViewController.h"


@interface EditFavoritesViewController ()

@end

@implementation EditFavoritesViewController

- (void)viewDidLoad
{
    [super viewDidLoad];

    self.searchBar.delegate = self;

    - (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar {

        //dismiss keyboard
        [self.searchBar resignFirstResponder];

    }

    - (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar {

        //Enable the cancel button when the user touches the search field
        self.searchBar.showsCancelButton = TRUE;
    }

    - (void)searchBarTextDidEndEditing:(UISearchBar *)searchBar {

        //dsiable the cancel button when the user ends editing
        self.searchBar.showsCancelButton = FALSE;
    }

    - (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar {

        //dismiss keyboard
        [self.searchBar resignFirstResponder];


        //Strip the whitespace off the end of the search text
        NSString *searchText = [self.searchBar.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];


        //Check to make sure the field isnt empty and Query parse for username in the text field
        if (![searchText isEqualToString:@""]) {

            PFQuery *query = [PFUser query];
            [query whereKey:@"username" equalTo:searchText];
            [query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
                if (!error) {

                    //check to make sure the query actually found a user
                    if (objects.count > 0) {

                        //found a user
                        //set the user as the table views data source and reload the table view


                        //A user was not found, display error message
                    } else {

                        //no user found

                    }

                    [self.tableView reloadData];

                } else {

                    //error occurred
                }
            }];

        }


    }


    PFQuery *query = [PFUser query];
    [query orderByAscending:@"username"];
    [query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
        if (error) {
            NSLog(@"Error: %@ %@", error, [error userInfo]);
        }
        else {
            self.allUsers = objects;
            [self.tableView reloadData];
        }
    }];

    self.currentUser = [PFUser currentUser];
}

#pragma mark - Table view data source

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    // Return the number of sections.
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    // Return the number of rows in the section.
    return [self.allUsers count];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"Cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];

    PFUser *user = [self.allUsers objectAtIndex:indexPath.row];
    cell.textLabel.text = user.username;

    if ([self isFriend:user]) {
        cell.accessoryType = UITableViewCellAccessoryCheckmark;
    }
    else {
        cell.accessoryType = UITableViewCellAccessoryNone;
    }

    return cell;
}

#pragma mark - Table view delegate

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    [self.tableView deselectRowAtIndexPath:indexPath animated:NO];

    UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];

    PFRelation *friendsRelation = [self.currentUser relationforKey:@"friendsRelation"];
    PFUser *user = [self.allUsers objectAtIndex:indexPath.row];

    if ([self isFriend:user]) {
        cell.accessoryType = UITableViewCellAccessoryNone;

        for(PFUser *friend in self.friends) {
            if ([friend.objectId isEqualToString:user.objectId]) {
                [self.friends removeObject:friend];
                break;
            }
        }

        [friendsRelation removeObject:user];
    }
    else {
        cell.accessoryType = UITableViewCellAccessoryCheckmark;
        [self.friends addObject:user];
        [friendsRelation addObject:user];
    }

    [self.currentUser saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
        if (error) {
            NSLog(@"Error: %@ %@", error, [error userInfo]);
        }
    }];
}

#pragma mark - Helper methods

- (BOOL)isFriend:(PFUser *)user {
    for(PFUser *friend in self.friends) {
        if ([friend.objectId isEqualToString:user.objectId]) {
            return YES;
        }
    }

    return NO;
}

@end</p>
            ```
Stone Preston
Stone Preston
42,016 Points

hmm your code looks good so im not really sure whats going on

Could it be that i targeting ios7?

Stone Preston
Stone Preston
42,016 Points

no im targetings ios 7 as well and it works fine

Ok...Thanks anyway man! So it can't be a problem of what type of search bar I use?

Did you use Parse Query Table setup?

Ben Jakuben
Ben Jakuben
Treehouse Teacher

Sorry I missed this, helloworld! Is it still an issue? The code you posted looks okay. If it's still a problem, let's start a new thread. Tag me in the new one so I get a notification.

:(

can you help for android?

Ben Jakuben
Ben Jakuben
Treehouse Teacher

Hi Fuseini Isaac, post a new question for Android and add some specific details and we can try to help.

Hi Ben Jakuben

I have the same problem with this code. It dosent find any user when I search

            <p>#import "FindViewController.h"
#import "FindDetailsViewController.h"
#import "FindCell.h"

@interface FindViewController ()

@end

@implementation FindViewController {

    //
    CLLocationManager *locationManager;
}

@synthesize currentGeoPoint;

- (id)initWithCoder:(NSCoder *)aCoder
{
    self = [super initWithCoder:aCoder];
    if (self) {


    }
    return self;
}


- (void)viewDidLoad
{


    //Sök
    self.searchBar.delegate = self;


    //Hittar GeoLocation för telefonen
    locationManager = [[CLLocationManager alloc] init];
    locationManager.distanceFilter = kCLDistanceFilterNone; // whenever we move
    locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters; // 100 m
    [locationManager startUpdatingLocation];

    NSLog(@"%@", [self deviceLocation]);

    self.tableView.backgroundColor = [UIColor clearColor];
    self.tableView.opaque = NO;
    self.tableView.backgroundView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"berg.jpg"]];


    self.navigationItem.hidesBackButton = YES;

    UIImage *image = [UIImage imageNamed: @"Invest.png"];
    UIImageView *imageView = [[UIImageView alloc] initWithImage: image];


    self.navigationItem.titleView = imageView;

    self.tableView.backgroundColor = [UIColor clearColor];
    self.tableView.opaque = NO;
    self.tableView.backgroundView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"berg.jpg"]];

    [super viewDidLoad];

    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(refreshTable:)
                                                 name:@"refreshTable"
                                               object:nil];
}

- (NSString *)deviceLocation {
    return [NSString stringWithFormat:@"latitude: %f longitude: %f", locationManager.location.coordinate.latitude, locationManager.location.coordinate.longitude];
}

- (void)refreshTable:(NSNotification *) notification
{
    // Reload the recipes
    [self loadObjects];
}


- (void)viewDidUnload
{
    [super viewDidUnload];



    // Release any retained subviews of the main view.
    [[NSNotificationCenter defaultCenter] removeObserver:self name:@"refreshTable" object:nil];
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}

- (void)loadObjects
{
    if (!self.currentGeoPoint)
    {
        [PFGeoPoint geoPointForCurrentLocationInBackground:^(PFGeoPoint *geo, NSError *error)
         {
             self.currentGeoPoint = geo;
             [super loadObjects];
         }];
    }
    else
    {
        [super loadObjects];
    }
}


- (PFQuery *)queryForTable
{
    PFQuery *query = [PFUser query];
    query.limit = 3;
    [query whereKey:@"money" equalTo: [NSNumber numberWithBool:TRUE]];
    [query whereKey:@"currentLocation" nearGeoPoint:self.currentGeoPoint withinKilometers:20];
    return query;


    self.pullToRefreshEnabled = YES;

    // Whether the built-in pagination is enabled
    self.paginationEnabled = NO;

    // The number of objects to show per page
    self.objectsPerPage = 10;

    return query;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath object:(PFObject *)object
{
    static NSString *simpleTableIdentifier = @"FindCell";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableIdentifier];
    }

    // Configure the cell
    PFFile *thumbnail = [object objectForKey:@"profileimageFile"];
    PFImageView *thumbnailImageView = (PFImageView*)[cell viewWithTag:100];
    thumbnailImageView.image = [UIImage imageNamed:@"placeholder2.jpg"];
    thumbnailImageView.file = thumbnail;
    [thumbnailImageView loadInBackground];

    UILabel *nameLabel = (UILabel*) [cell viewWithTag:101];
    nameLabel.text = [object objectForKey:@"name"];


    UILabel *prepTimeLabel = (UILabel*) [cell viewWithTag:102];
    prepTimeLabel.text = [object objectForKey:@"title"];

    return cell;
}


- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
    return NO;
}


- (void) objectsDidLoad:(NSError *)error
{

    [super objectsDidLoad:error];



    NSLog(@"error: %@", [error localizedDescription]);
}

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    if ([segue.identifier isEqualToString:@"showFindDetails"]) {
        NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
        FindDetailsViewController *destViewController = segue.destinationViewController;

        PFObject *object = [self.objects objectAtIndex:indexPath.row];
        FindCell *find = [[FindCell alloc] init];
        find.name = [object objectForKey:@"name"];
        find.imageFile = [object objectForKey:@"profileimageFile"];
        find.prepTime = [object objectForKey:@"title"];
        find.location = [object objectForKey:@"location"];
        find.ingredients = [object objectForKey:@"moneyProfile"];
        destViewController.find= find;

    }
}

- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar {

    //dismiss keyboard
    [self.searchBar resignFirstResponder];

}

- (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar {

    //Enable the cancel button when the user touches the search field
    self.searchBar.showsCancelButton = TRUE;
}

- (void)searchBarTextDidEndEditing:(UISearchBar *)searchBar {

    //dsiable the cancel button when the user ends editing
    self.searchBar.showsCancelButton = FALSE;
}

- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar {

    //dismiss keyboard
    [self.searchBar resignFirstResponder];


    //Strip the whitespace off the end of the search text
    NSString *searchText = [self.searchBar.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];


    //Check to make sure the field isnt empty and Query parse for username in the text field
    if (![searchText isEqualToString:@""]) {

        PFQuery *query = [PFUser query];
        [query whereKey:@"username" equalTo:searchText];
        [query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
            if (!error) {

                //check to make sure the query actually found a user
                if (objects.count > 0) {

                    //found a user
                    //set the user as the table views data source and reload the table view


                    //A user was not found, display error message
                } else {

                    //no user found

                }

                [self.tableView reloadData];

            } else {

                //error occurred
            }
        }];

    }


}


@end</p>
            ```