Search bar with NSPredicate doesn't work - ios

I've made a view for editing friend, with a searchbar. My adding/deleting friends is working fine, but I've a problem with my adding/deleting friends WITH SEARCHBAR...
My searchbar finds well the email I'm tapping, but the order change :
if I find 2 email with my searchbar, the 2 email will are the 2 first email in my property "Allfriend", and not the email I found with searchbar...
Is there any code to complete in didSelectRowAtIndexPath after NSLog ?
I let you see my code, tell me if you see a problem :
editfriend.h :
#import <UIKit/UIKit.h>
#import <Parse/Parse.h>
#interface EditFriendsViewController : UITableViewController <UISearchBarDelegate, UISearchDisplayDelegate>
#property (nonatomic, strong) NSArray *allUsers;
#property (nonatomic, strong) PFUser *currentUser;
#property (nonatomic, strong) NSMutableArray *friends;
#property (strong, nonatomic) NSArray *searchResults;
#property (nonatomic, strong) PFUser *user;
#property (nonatomic, strong) NSMutableArray *filteredArray;
#property (nonatomic, strong) UIImage *MindleNav;
-(BOOL)isFriend:(PFUser*)user;
#end
editfriend.m :
#import "EditFriendsViewController.h"
#interface EditFriendsViewController ()
#end
#implementation EditFriendsViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.navigationItem.titleView = [[UIImageView alloc] initWithImage:self.MindleNav];
self.searchResults = [[NSArray alloc] init];
PFQuery *query = [PFUser query];
[query orderByAscending:#"email"];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (error) {
NSLog(#"Error: %# %#", error, [error userInfo]);
}
else {
self.allUsers = objects;
// self.user = [objects objectAtIndex:0];
[self.tableView performSelectorOnMainThread:#selector(reloadData) withObject:nil waitUntilDone:NO];
}
}];
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.
if (tableView == self.searchDisplayController.searchResultsTableView)
{
return [self.searchResults count];
}
else
{
return [self.allUsers count];
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
PFUser *user = [self.allUsers objectAtIndex:indexPath.row];
if (tableView == self.searchDisplayController.searchResultsTableView) {
cell.textLabel.text = [[self.searchResults objectAtIndex:indexPath.row] email];
} else {
cell.textLabel.text = user.email;
}
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 (tableView == self.searchDisplayController.searchResultsTableView) {
NSLog(#"CLICK search");
cell.accessoryType = UITableViewCellAccessoryNone;
} else {
NSLog(#"CLICK");
}
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
- (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope
{
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"email beginswith[c] %#", searchText];
self.searchResults = [self.allUsers filteredArrayUsingPredicate:predicate];
}
-(BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString
{
[self filterContentForSearchText:searchString
scope:[[self.searchDisplayController.searchBar scopeButtonTitles]
objectAtIndex:[self.searchDisplayController.searchBar
selectedScopeButtonIndex]]];
return YES;
}
- (BOOL)isFriend:(PFUser *)user {
for(PFUser *friend in self.friends) {
if ([friend.objectId isEqualToString:user.objectId]) {
return YES;
}
}
return NO;
}
#end

Your array (probably) contains PFUser objects and the predicate is:
[NSPredicate predicateWithFormat:#"SELF beginswith[c] %#", searchText]
BEGINSWITH works only for strings and you try to apply it to PFUser objects. This is a reason of the crash. If you want to search for users whose names begin with the searchText, change the predicate to:
[NSPredicate predicateWithFormat:#"username beginswith[c] %#", searchText]

Related

Error in ios8 app- SearchBar is searching but not displaying data on the screen

I am trying to make an ios app on xcode by following this - http://www.appcoda.com/search-bar-tutorial-ios7/. When I type in search bar everything works in console but output is not getting displayed on the screen
#interface AllUseraStatusViewController ()
#property (weak, nonatomic) IBOutlet UITableView *statusText;
#property (weak, nonatomic) IBOutlet UITableView *statusTime;
#property (weak, nonatomic) IBOutlet UITableView *statusUserName;
#property (strong,nonatomic) NSArray *statusList;
#property (strong,nonatomic) NSMutableArray *allStatusLists;
#property(strong,nonatomic) NSArray *searchResults;
#property(strong,nonatomic) PFObject *object;
#end
#implementation AllUseraStatusViewController
- (void)viewDidLoad {
[super viewDidLoad];
PFQuery *query = [PFQuery queryWithClassName:#"Status"];
[query includeKey:#"user"];
[query orderByDescending:#"createdAt"];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
// 3
if (!error){
self.statusList = objects;
[self.tableView reloadData];
} else {
// 4
[[[UIAlertView alloc] initWithTitle:#"Error" message:[error userInfo][#"error"] delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil] show];
}
}];
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 70;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if(tableView==self.searchDisplayController.searchResultsTableView)
{
return [self.searchResults count];
}else{
return [self.statusList count];
}
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *simpleTableIdentifier= #"AllUsersStatusCell";
UITableViewCell *cell =[tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell==nil){
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableIdentifier];
}
if (tableView == self.searchDisplayController.searchResultsTableView){
self.object =[self.searchResults objectAtIndex:indexPath.item];
NSLog(#"THIS IS object %#",self.object);
}else{
self.object =[self.statusList objectAtIndex:indexPath.item];
}
NSLog(#"THIS IS objectttt %#",self.object); <--this prints correct status in console
UILabel *statusText = (UILabel *)[cell viewWithTag:100];
[statusText setText:[self.object objectForKey:#"status"]];
NSLog(#"THIS IS objectttt %#",statusText);<--but this prints null in console
UILabel *statusTime = (UILabel *)[cell viewWithTag:101];
[statusTime setText:[[self.object createdAt] description]];
UILabel *statusUserName = (UILabel *)[cell viewWithTag:102];
PFUser *userName = [self.object objectForKey:#"user"];
[statusUserName setText:userName.username];
return cell;
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"detail"]) {
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
UserProfileViewController *vc = segue.destinationViewController;
PFObject *object = [self.statusList objectAtIndex:indexPath.row];
vc.statusObject=object;
}
}
-(BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString
{
[self filterContentForTextSearch:searchString
scope:[[self.searchDisplayController.searchBar scopeButtonTitles]
objectAtIndex:[self.searchDisplayController.searchBar selectedScopeButtonIndex]]];
return YES;
}
-(void)filterContentForTextSearch:(NSString *)searchText scope:(NSString *)scope
{
NSPredicate *resultPredicate=[NSPredicate predicateWithFormat:#"status contains[c] %#",searchText];
NSLog(#"THIS IS %#",resultPredicate);
self.searchResults = [self.statusList filteredArrayUsingPredicate:resultPredicate];
NSLog(#"THIS IS THE %#", self.searchResults);
}
#end
MAKING THE ERROR MORE CLEAR
This is the error in the above code
NSLog(#"THIS IS object %#",self.object);// this prints correct object
UILabel *statusText = (UILabel *)[cell viewWithTag:100];
[statusText setText:[self.object objectForKey:#"status"]];
NSLog(#".....THIS IS objectt %#",statusText.text);// it prints null on console
NSLog(#".....THIS IS objectttt %#",[self.object objectForKey:#"status"]);// this prints correct o/p

NSPredicate search through NSMutableArray

I have a view controller that retrieves address information from my web service, stores it in a mutable array and then displays each address in a table view. I have a search bar on the same view controller that i'd like to search through each of the addresses and display the results. I have this working with a test NSArray, however I'm not sure what I need to do to the filterContentForSearchText function to get it to search through an NSMutableArray. Any help appreciated.
Object Class
// Branches.h
#interface Branches : NSObject
#property (nonatomic, retain) NSString *BranchAddress;
#end
View Controller Class
// ViewController.h
#interface ViewController : UIViewController <UITableViewDataSource, UITableViewDelegate, UISearchDisplayDelegate>
#property (weak, nonatomic) IBOutlet UITableView *tableView;
#end
.
// ViewController.m
#import "AFHTTPRequestOperationManager.h"
#import "ViewController.h"
#import "Branches.h"
#interface ViewController () {
NSMutableArray *array;
}
#property (strong, nonatomic) NSArray *searchResults;
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Initialize myArray
array = [[NSMutableArray alloc] init];
// Set POST parameters
NSDictionary *parameters = #{#"key" : #"value"};
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
[manager POST:#"webservice_address" parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject) {
// Check to see if responseObject contains data
if (responseObject != nil) {
// Loop through JSON
for (NSDictionary *dictionary in responseObject) {
// Initialize object
Branches *branches = [[Branches alloc] init];
branches.branchAddress = [dictionary objectForKey:#"Object"];
// Add object to myArray
[array addObject:branches];
[self.tableView reloadData];
}
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
}
#pragma Table View Methods
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (tableView == self.searchDisplayController.searchResultsTableView) {
return [self.searchResults count];
} else {
return [array count];
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellID = #"cellID";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellID];
}
if (tableView == self.searchDisplayController.searchResultsTableView) {
cell.textLabel.text = [self.searchResults objectAtIndex:indexPath.row];
} else {
cell.textLabel.text = [[array objectAtIndex:indexPath.row] BranchAddress];
}
return cell;
}
#pragma Search Methods
- (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope
{
NSArray *masterArray = array;
NSArray *searchResults = [masterArray filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(Branches *evaluatedObject, NSDictionary *bindings) {
//NSLog(#"%#", evaluatedObject.BranchAddress);
return ([evaluatedObject.BranchAddress rangeOfString: searchText options:NSCaseInsensitiveSearch].location != NSNotFound);
}]];
NSLog(#" %i", searchResults.count);
//[searchResults removeAllObjects];
//[searchResults addObjectsFromArray:searchResults];
//reload after this
NSLog(#"%#", [searchResults objectAtIndex:0]);
}
-(BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString
{
[self filterContentForSearchText:searchString
scope:[[self.searchDisplayController.searchBar scopeButtonTitles]
objectAtIndex:[self.searchDisplayController.searchBar
selectedScopeButtonIndex]]];
return YES;
}
#end
u can try this
- (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope
{
NSArray *masterArray = self.array;
NSArray *resultsArray = [masterArray filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(Branches *evaluatedObject, NSDictionary *bindings) {
return ([evaluatedObject.BranchAddress rangeOfString: searchText options:NSCaseInsensitiveSearch].location != NSNotFound);
}]];
//edited
[self.searchResults removeAllObjects]; //self.searchResults should be mutable array
[self.searchResults addObjectsFromArray:resultsArray];//put the new values to searchResults
//after this self.searchResults contains objects of filtered Branches u can get the values for example
NSLog(#"%#", [[self.searchResults objectAtIndex:0] BranchAddress]);//self.searchResults contains objects of Branches not the string itself
//reload after this
}

cell.accessoryType with search bar don't affect with NSPredicate

I've made a view for editing friends with search bar. Everything works fine, but when I find users with search bar, cell.accessoryType isn't right. (My cell.accessoryType is good when I don't use search bar).
Checkmark isn't for the right user.
I think it's a problem in "isFriend" action ?
Here is my code :
editfriends.h :
#import <UIKit/UIKit.h>
#import <Parse/Parse.h>
#interface EditFriendsViewController : UITableViewController <UISearchBarDelegate, UISearchDisplayDelegate>
#property (nonatomic, strong) NSArray *allUsers;
#property (nonatomic, strong) PFUser *currentUser;
#property (nonatomic, strong) NSMutableArray *friends;
#property (strong, nonatomic) NSArray *searchResults;
#property (nonatomic, strong) PFUser *user;
#property (nonatomic, strong) UIImage *MindleNav;
-(BOOL)isFriend:(PFUser*)user;
#end
editfriends.m :
#import "EditFriendsViewController.h"
#interface EditFriendsViewController ()
#end
#implementation EditFriendsViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.navigationItem.titleView = [[UIImageView alloc] initWithImage:self.MindleNav];
self.searchResults = [[NSArray alloc] init];
PFQuery *query = [PFUser query];
[query orderByAscending:#"email"];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (error) {
NSLog(#"Error: %# %#", error, [error userInfo]);
}
else {
self.allUsers = objects;
// self.user = [objects objectAtIndex:0];
[self.tableView performSelectorOnMainThread:#selector(reloadData) withObject:nil waitUntilDone:NO];
}
}];
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.
if (tableView == self.searchDisplayController.searchResultsTableView)
{
return [self.searchResults count];
}
else
{
return [self.allUsers count];
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
PFUser *user = [self.allUsers objectAtIndex:indexPath.row];
if (tableView == self.searchDisplayController.searchResultsTableView) {
cell.textLabel.text = [[self.searchResults objectAtIndex:indexPath.row] email];
} else {
cell.textLabel.text = user.email;
}
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"];
//Edit Friends with searchbar
if (tableView == self.searchDisplayController.searchResultsTableView) {
PFUser *user = [self.searchResults objectAtIndex:indexPath.row];
if ([self isFriend:user]) {
cell.accessoryType = UITableViewCellAccessoryNone;
for(PFUser *friend in self.searchResults) {
if ([friend.objectId isEqualToString:user.objectId]) {
[self.friends removeObject:friend];
[self.tableView performSelectorOnMainThread:#selector(reloadData) withObject:nil waitUntilDone:NO];
break;
}
}
[friendsRelation removeObject:user];
}
else {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
[self.friends addObject:user];
[friendsRelation addObject:user];
[self.tableView performSelectorOnMainThread:#selector(reloadData) withObject:nil waitUntilDone:NO];
}
[self.currentUser saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (error) {
NSLog(#"Error: %# %#", error, [error userInfo]);
}
}];
//Edit Friends
} else {
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
- (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope
{
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"email beginswith[c] %#", searchText];
self.searchResults = [self.allUsers filteredArrayUsingPredicate:predicate];
}
-(BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString
{
[self filterContentForSearchText:searchString
scope:[[self.searchDisplayController.searchBar scopeButtonTitles]
objectAtIndex:[self.searchDisplayController.searchBar
selectedScopeButtonIndex]]];
return YES;
}
- (BOOL)isFriend:(PFUser *)user {
for(PFUser *friend in self.friends) {
if ([friend.objectId isEqualToString:user.objectId]) {
return YES;
}
}
return NO;
}
#end
It would appear to be because you're using the wrong user object to check the status. Compare with:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
NSArray *sourceData = (tableView == self.searchDisplayController.searchResultsTableView ? self.searchResults : self.allUsers);
PFUser *user = [sourceData objectAtIndex:indexPath.row];
cell.textLabel.text = user.email;
if ([self isFriend:user]) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
} else {
cell.accessoryType = UITableViewCellAccessoryNone;
}
return cell;
}

Search bar is working AFTER "Cancel Button"

I've made a view for editing friend, with a searchbar. My adding/deleting friends is working fine, but I've a problem with my adding/deleting friends WITH SEARCHBAR...
My searchbar finds well the email I'm tapping, but the order change :
if I find 2 email with my searchbar, the 2 email will are the 2 first email in my property "Allfriend", and not the email I found with searchbar...
Is there any code to complete in didSelectRowAtIndexPath after NSLog ?
I let you see my code, tell me if you see a problem :
editfriend.h :
#import <UIKit/UIKit.h>
#import <Parse/Parse.h>
#interface EditFriendsViewController : UITableViewController <UISearchBarDelegate, UISearchDisplayDelegate>
#property (nonatomic, strong) NSArray *allUsers;
#property (nonatomic, strong) PFUser *currentUser;
#property (nonatomic, strong) NSMutableArray *friends;
#property (strong, nonatomic) NSArray *searchResults;
#property (nonatomic, strong) PFUser *user;
#property (nonatomic, strong) NSMutableArray *filteredArray;
#property (nonatomic, strong) UIImage *MindleNav;
-(BOOL)isFriend:(PFUser*)user;
#end
editfriend.m :
#import "EditFriendsViewController.h"
#interface EditFriendsViewController ()
#end
#implementation EditFriendsViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.navigationItem.titleView = [[UIImageView alloc] initWithImage:self.MindleNav];
self.searchResults = [[NSArray alloc] init];
PFQuery *query = [PFUser query];
[query orderByAscending:#"email"];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (error) {
NSLog(#"Error: %# %#", error, [error userInfo]);
}
else {
self.allUsers = objects;
// self.user = [objects objectAtIndex:0];
[self.tableView performSelectorOnMainThread:#selector(reloadData) withObject:nil waitUntilDone:NO];
}
}];
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.
if (tableView == self.searchDisplayController.searchResultsTableView)
{
return [self.searchResults count];
}
else
{
return [self.allUsers count];
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
PFUser *user = [self.allUsers objectAtIndex:indexPath.row];
if (tableView == self.searchDisplayController.searchResultsTableView) {
cell.textLabel.text = [[self.searchResults objectAtIndex:indexPath.row] email];
} else {
cell.textLabel.text = user.email;
}
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"];
//Edit Friends with searchbar
if (tableView == self.searchDisplayController.searchResultsTableView) {
PFUser *user = [self.searchResults objectAtIndex:indexPath.row];
if ([self isFriend:user]) {
cell.accessoryType = UITableViewCellAccessoryNone;
[self.tableView performSelectorOnMainThread:#selector(reloadData) withObject:nil waitUntilDone:NO];
for(PFUser *friend in self.searchResults) {
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.tableView performSelectorOnMainThread:#selector(reloadData) withObject:nil waitUntilDone:NO];
}
[self.currentUser saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (error) {
NSLog(#"Error: %# %#", error, [error userInfo]);
}
}];
//Edit Friends
} else {
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
- (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope
{
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"email beginswith[c] %#", searchText];
self.searchResults = [self.allUsers filteredArrayUsingPredicate:predicate];
}
-(BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString
{
[self filterContentForSearchText:searchString
scope:[[self.searchDisplayController.searchBar scopeButtonTitles]
objectAtIndex:[self.searchDisplayController.searchBar
selectedScopeButtonIndex]]];
return YES;
}
- (BOOL)isFriend:(PFUser *)user {
for(PFUser *friend in self.friends) {
if ([friend.objectId isEqualToString:user.objectId]) {
return YES;
}
}
return NO;
}
#end
You're calling [self.tableView reloadData] on a background thread (due to findObjectsInBackground), so your UI won't update right away.
If you want the changes to take place instantly, you need to rewrite that line to:
[self.tableView performSelectorOnMainThread:#selector(reloadData) withObject:nil waitUntilDone:NO];
Also, since you already do a query for all objects in viewDidLoad, when the user would like to search, you shouldn't query again, but instead have a property called filteredUsers, which holds the user you'd like to display.
You can filter an array with:
self.filteredArray = [self.allUsers filteredArrayUsingPredicate:predicate];
You can take a look here on how to create an NSPredicate.

Parse UITableView with Search

I'm following this AppCoda tutorial on implementing Search; however, I'm pulling titles for the table view from Parse and can't get the search function to work. Throws an exception when I start typing in the search:
'Can't use in/contains operator with collection {
buildingLat = "42.726366";
buildingLong = "-84.480642";
buildingTitle = "International Center";
} (not a collection)'
Here's the code for my table view controller:
#import "BuildingsViewController.h"
#import <Parse/Parse.h>
#interface BuildingsViewController ()
#end
#implementation BuildingsViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
[self performSelector:#selector(retrieveBuildings)];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - TableView Setup
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [self.buildingsArray count];
if (tableView == self.searchDisplayController.searchResultsTableView) {
return [self.searchResults count];
} else {
return [self.buildingsArray count];
}
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"buildingsCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
PFObject *tempObject = [self.buildingsArray objectAtIndex:indexPath.row];
cell.textLabel.text = [tempObject objectForKey:#"buildingTitle"];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
if (tableView == self.searchDisplayController.searchResultsTableView) {
cell.textLabel.text = [self.searchResults objectAtIndex:indexPath.row];
} else {
cell.textLabel.text = [tempObject objectForKey:#"buildingTitle"];
}
return cell;
}
#pragma mark - Helper Methods
-(void)retrieveBuildings
{
PFQuery *retrieveBuildings = [PFQuery queryWithClassName:#"buildingsList"];
[retrieveBuildings findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
self.buildingsArray = [[NSArray alloc] initWithArray:objects];
}
[self.tableView reloadData];
}];
}
- (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope
{
NSPredicate *resultPredicate = [NSPredicate
predicateWithFormat:#"SELF contains[cd] %#",
searchText];
self.searchResults = [self.buildingsArray filteredArrayUsingPredicate:resultPredicate];
}
-(BOOL)searchDisplayController:(UISearchDisplayController *)controller
shouldReloadTableForSearchString:(NSString *)searchString
{
[self filterContentForSearchText:searchString
scope:[[self.searchDisplayController.searchBar scopeButtonTitles]
objectAtIndex:[self.searchDisplayController.searchBar
selectedScopeButtonIndex]]];
return YES;
}
#end
I think you are on the right track...
My understanding is that you are receiving an array of dictionaries, and passing that received array to your declared buildingsArray. This is not the issue. The issue in my understanding is that you are then attempting to retrieve one of the values from one of those dictionaries without the appropriate code.
Your are attempting this process in two locations.
I refer to the code within your cellForRowAtIndexPath method.
As an aside, it is no longer necessary to check for cell == nil, so you can remove the if statement that wraps your cell setter (cell = [[UITableViewCell...).
UPDATE...
The crash in your code when you remove this check for nil is due to the fact that you do not register a reuse identifier for the searchResultsTableView.
To correct your search and data parsing, I recommend that you follow the sample code I have included following.
Add a new property tempMutableArray, and remove the static declaration from cellForRowAtIndexPath and place it between the #import and #interface lines as shown following...
#import <Parse/Parse.h>
static NSString *CellIdentifier = #"buildingsCell"; // relocated static declaration
#interface BuildingsViewController ()
#property (nonatomic, strong) NSMutableArray *tempMutableArray;
#end
// implementation
Then in your viewDidLoad TVC lifecycle method, instantiate the NSMutableArray, and register the UITableViewCell class and CellIdentifier reuse identifier with searchResultsTableView...
- (void)viewDidLoad {
[super viewDidLoad];
//...your other code...
[self setTempMutableArray:[[NSMutableArray alloc] init]];
[self.searchDisplayController.searchResultsTableView registerClass:[UITableViewCell class]
forCellReuseIdentifier:CellIdentifier];
}
...
YOUR NEW REPLACEMENT cellForRowAtIndexPath: METHOD
To properly parse your information from Parse, try the following...
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = nil;
if (tableView == self.searchDisplayController.searchResultsTableView) {
cell = [self.searchDisplayController.searchResultsTableView dequeueReusableCellWithIdentifier:CellIdentifier];
cell.textLabel.text = [self.searchResults objectAtIndex:indexPath.row];
} else {
cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
NSDictionary *tempDict = nil;
NSString *tempString = nil;
tempDict = [self.buildingsArray objectAtIndex:indexPath.row];
tempString = [tempDict objectForKey:#"buildingTitle"];
[cell.textLabel setText:tempString];
}
return cell;
}
You will also need to complete similar in your filterContentForSearchText: method.
YOUR NEW REPLACEMENT filterContentForSearchText: METHOD
- (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope
{
NSPredicate *resultPredicate = nil;
[self.tempMutableArray removeAllObjects];
for (NSDictionary *tempDict in self.buildingsArray) {
NSString *tempString = nil;
tempString = [tempDict objectForKey:#"buildingTitle"];
[self.tempMutableArray addObject:tempString];
}
resultPredicate = [NSPredicate predicateWithFormat:#"SELF contains[cd] %#", searchText];
self.searchResults = [self.tempMutableArray filteredArrayUsingPredicate:resultPredicate];
}
...CONTINUE WITH PREVIOUS RESPONSE
My understanding... what is happening in your code is that your PFQuery returns an NSArray in its completion block. You set your property buildingsArray, also an NSArray, based on this. Your no longer need, as far as I understand, to treat the returned data as a PFObject.
Let me know how you go.
You are trying to apply the NSPredicate on an array of PFObjects so your predicate needs to look like this:
NSPredicate *resultPredicate = [NSPredicate
predicateWithFormat:#"variableNameToSearchOn contains[cd] %#",
searchText];
EDIT:
You can try this:
NSPredicate *resultPredicate = [NSPredicate
predicateWithFormat:#"buildingTitle contains[c] %#",
searchText];
#pragma mark UISearchBarDelegate
-(void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar{
sar.showsCancelButton=YES;
sar.autocorrectionType = UITextAutocorrectionTypeNo;
}
- (BOOL)searchBar:(UISearchBar *)searchBar shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
{
NSString* newText = [searchBar.text stringByReplacingCharactersInRange:range withString:text];
searchStr = newText;
[self DisplayMatchSearch];
[tblView reloadData];
return YES;
}
-(BOOL)searchBarShouldEndEditing:(UISearchBar *)searchBar{
//write code for requset data from database search.....
sar.showsCancelButton=NO;
[sar resignFirstResponder];
return YES;
}
-(void)searchBarCancelButtonClicked:(UISearchBar *)searchBar{
[self getAlldata];
#try {
[tblBeepUsers reloadData];
}
#catch (NSException * e) {
}
[sar resignFirstResponder];
sar.text=#"";
}
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar{
searchStr = sar.text;
[sar resignFirstResponder];
[self DisplayMatchSearch];
[tblView reloadData];
}
-(void)DisplayMatchSearch{
if (searchStr && searchStr.length) {
//temp array
arrLocation = [[NSMutableArray alloc] init];
//parsed array self.buildingsArray
for (NSDictionary *dictionary in self.buildingsArray)
{
if ([[dictionary objectForKey:#"name"] rangeOfString:searchStr options:NSCaseInsensitiveSearch].location != NSNotFound) {
[arrLocation addObject:dictionary];
}
}
[tblview reloadData];
}
}

Resources