3

Scenario = I have an app that allows users to search for other users that use the service. In the search page there is a UISearchDisplayController that when a user begins typing in the search bar, a tableView will programmatically appear (just like any other UISearchDisplayController) and filter all of the users in the database depending on whats being typed ('begins-with'). So the user will begin typing, "B... r...." and users will begin to populate the tableView from "Brad" to "Brandon" and so on based on the text being inputted.

Question = How would one go about designing the parse query to achieve this effect?

Specific Questions =

1) When and Where to begin the initial query?...

PFQuery *searchQuery = [PFUser query];
[searchQuery whereKey:@"username" containsString:controller.searchBar.text];
[searchQuery orderByDescending:@"updatedAt"];
[searchQuery findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {

    NSLog(@"%@", objects);

    searchArray = objects;

}];

in "searchDisplayControllerDidBeginSearch"?

2) When and Where do I put the logic to fill in the tableView?

PFObject *searchObject = [searchArray objectAtIndexPath:indexPath.row];
cell.nameLabel.text = searchObject[@"name"];

in "cellForRowAtIndexPath"?

If there is anyone out there that knows this and can help me out Id appreciate it.

1 Answer 1

6

Here is a simple example:

#import <Parse/Parse.h>

@interface MySearchController : PFQueryTableViewController

@end

And implementation

#import "MySearchController.h"

@interface MySearchController() <UISearchBarDelegate, UISearchDisplayDelegate>

@property (strong, nonatomic) IBOutlet UISearchBar *searchBar;
@property (nonatomic, strong) NSMutableArray *searchResults;

@end

@implementation MySearchController

- (id)initWithCoder:(NSCoder *)aCoder
{
    self = [super initWithCoder:aCoder];
    if (self) {
        // get users
        self.parseClassName = [PFUser parseClassName];
        self.pullToRefreshEnabled = YES;
        self.paginationEnabled = YES;
        self.objectsPerPage = 10;

        self.searchResults = [NSMutableArray new];
    }
    return self;
}

- (void)filterResults:(NSString *)searchTerm {

    [self.searchResults removeAllObjects];

    for (PFUser *user in self.objects)
    {
        NSString *username = user.username;
        if ([[username lowercaseString] hasPrefix:[searchTerm lowercaseString]])
        {
            [self.searchResults addObject:user];
        }
    }
}

- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:  (NSString *)searchString {
    [self filterResults:searchString];
    return YES;
}

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return (tableView == self.tableView) ? self.objects.count :  self.searchResults.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    PFUser *user = (tableView == self.tableView) ? self.objects[indexPath.row] : self.searchResults[indexPath.row];

    static NSString *identifier = @"reuseIdentifier";
    UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:identifier];
    if (!cell)
    {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
                                      reuseIdentifier:identifier];
    }

    cell.textLabel.text = user.username;

    return cell;
}


@end

The main thing to note about this that threw me off is that you have two table views, so you have to be careful. One of the table views is from the original query, it will give you all users, that one is self.tableView. The other is from the search results, self.searchDisplayController.searchResultsTableView. The latter is active while searching. Therefore, you must return different values for each regular tableviewcontroller method. The number of rows is either self.objects.count or self.searchResults.count. The correct user is either self.objects[indexPath.row] or self.searchResults[indexPath.row]. It is easy to check which table view you're dealing with in a given protocol method, just use this condition: (tableView == self.tableView)

Sign up to request clarification or add additional context in comments.

9 Comments

Thank you so much for your response doctor! Please note though, in my case this is just a regular view controller with a UISearchDisplayController search bar on it. Not a PFQueryViewController as in your example (though I am querying for Parse data). Does your example still apply to my case? And if so, is what is happening just the UISearchDisplayController's tableView is just "using" the PFQueryViewController's fetch methods to populate itself?
@TomTesticool Great name btw, look into PFQueryTableViewController, it comes with a lot of really cool stuff for Parse queries built in: pull-to-refresh being the coolest I think. And yes, it also has a method -(PFQuery*)queryForTable that it uses to fetch objects. This method is implicitly called and it uses the self.parseClassName field for its query in a simple return [query withParseClassName:self.parseClassName]. I would suggest you use this class, but everything is still possible in a uiviewcontroller. Try dropping my class into your storyboard, and see how effective it is.
Thanks, my mother gave it to me... I love me some PFQueryViewControllers as well. Thing is the VC that I'm using is doing more than just being a table view though. I got a collection and other things going on too. So I'm not sure if that's going to be an option. But if what I hear you saying is all I have to do is subclass my VC from PFQueryTableViewController and just use it's queryForTable method to populate my search table view then I'm going to try that? Is that correct, or am I half retarded?
@TomTesticool Only half. The best thing you can do is keep one VC with all the other non-query stuff in it. Then put a container view in that vc, and embed your PFQueryTableViewController into that container view by control-clicking the container view and dragging it to a TableViewController whose class is your custom query class. In the prepareToSegue method of your containing vc, you can get a reference to the query table vc, segue.destinationViewController and then you can use that to mess with it, or retrieve data from it to update the container vc.
That's a good work around. I'm going to try some of this stuff and see where it takes me. I know I'm probably going to have to lay down a PFQTVC in the at some point anyways.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.