I am trying to search my array contents from the TableView,The Cells Have Detail View also.When i run my app in stimulator it runs but when i insert objects dynamically and try to search them app crashes with 'NSInvalidArgumentException'.
reason:
'Can't use in/contains operator with collection <customcells: 0x8ea3b40> (not a collection)'.
My code which has Predicates is
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText {
self.searchedarray = nil;
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF contains [cd] %#", self.searchtext.text];
self.searchedarray =[NSMutableArray arrayWithArray :[contactsarray filteredArrayUsingPredicate:predicate]];
}
and also
-(void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope {
[searchedarray removeAllObjects];
// Filter the array using NSPredicate
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"self.name CONTAINS[c] %#", searchText];
searchedarray = [NSMutableArray arrayWithArray:[contactsarray filteredArrayUsingPredicate:predicate]];
}
Help me, I am stuck with this Predicate error ;Using Like instead of contains also doesn't help.
Edited content
- (void)viewDidLoad
{
contactsarray = [[NSMutableArray alloc] init];
self.searchedarray = [NSMutableArray arrayWithCapacity:[contactsarray count]];
[super viewDidLoad];
self.navigationItem.rightBarButtonItem = self.editButtonItem;
UIBarButtonItem *addButton = [[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemAdd
target:self
action:#selector(insertNewObject:)];
self.navigationItem.rightBarButtonItem = addButton;
}
- (void)insertNewObject:(id)sender
{
customcells *new = [[customcells alloc] init];
new.name = #"Enter Name";
new.contacts=#" Enter Contact Number";
new.organisation=#"Enter Organisation Name";
[contactsarray insertObject:new atIndex:0];
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0];
[self.tableView insertRowsAtIndexPaths:#[indexPath]
withRowAnimation:UITableViewRowAnimationAutomatic];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
- (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 [searchedarray count];
} else {
return [contactsarray count];
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"cell";
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:CellIdentifier];
customcells *myarray= [contactsarray objectAtIndex:indexPath.row];
cell.textLabel.text = myarray.name;
cell.textLabel.text = [NSString stringWithFormat:#"%#",
myarray.name];
if(cell==nil)
{
cell=[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
if (tableView == self.searchDisplayController.searchResultsTableView)
{
customcells *newarray = [searchedarray objectAtIndex:indexPath.row];
cell.textLabel.text =newarray.name;
} else {
customcells *myarray= [contactsarray objectAtIndex:indexPath.row];
cell.textLabel.text = myarray.name;
cell.textLabel.text = [NSString stringWithFormat:#"%#",
myarray.name]; }
}
return cell;
}
// Override to support conditional editing of the table view.
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
// Return NO if you do not want the specified item to be editable.
return YES;
}
// Override to support editing the table view.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete) {
// Delete the row from the data source
NSMutableArray *tempContent = [self.contactsarray mutableCopy];
[tempContent removeObject:[tempContent objectAtIndex:indexPath.row]];
self.contactsarray = tempContent;
[tableView deleteRowsAtIndexPaths:[NSMutableArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
} else if (editingStyle == UITableViewCellEditingStyleInsert) {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
}
}
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText {
self.searchedarray = nil;
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF contains [cd] %#", self.searchtext.text];
self.searchedarray =[NSMutableArray arrayWithArray :[contactsarray filteredArrayUsingPredicate:predicate]];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"showDetail"])
{
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
customcells *new = contactsarray[indexPath.row];
[[segue destinationViewController] setDetailItem:new];
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
}
-(void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope {
[searchedarray removeAllObjects];
// Filter the array using NSPredicate
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"self.name CONTAINS[c] %#", searchText];
searchedarray = [NSMutableArray arrayWithArray:[contactsarray filteredArrayUsingPredicate:predicate]];
}
-(BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString {
// Tells the table data source to reload when text changes
[self filterContentForSearchText:searchString scope:
[[self.searchDisplayController.searchBar scopeButtonTitles] objectAtIndex:[self.searchDisplayController.searchBar selectedScopeButtonIndex]]];
// Return YES to cause the search result table view to be reloaded.
return YES;
}
-(BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchScope:(NSInteger)searchOption {
// Tells the table data source to reload when scope bar selection changes
[self filterContentForSearchText:self.searchDisplayController.searchBar.text scope:
[[self.searchDisplayController.searchBar scopeButtonTitles] objectAtIndex:searchOption]];
// Return YES to cause the search result table view to be reloaded.
return YES;
}
#end
Your code is using 'contains' in the NSPredicate, which assumes that each element in your array can be matched using 'contains'. You have an array of customcells, which will not by default have any way of dealing with a contains matcher.
In your second example, using self.name CONTAINS[c] %#, you end up matching on the name property which is a string, so I would expect this to work (though I've not tried it). The first example you give is trying to match on the customcells object directly, so I'd suggest also using self.name in that case as well.
Related
I've created a table view with search bar in my View Controller, but the table view doesn't show the search result.
This is how my code working. First i create an Array to hold data >> display the array data in table view >> using search bar to filter the table view.
Here is my code :
- (void)viewDidLoad {
[super viewDidLoad];
self.inventoryarray =[[NSArray alloc] initWithObjects: #"apple",#"samsung",#"HTC",#"LG",#"Sony",#"Motorola",#"Nexus",#"Asus" ,nil];
self.searchresult =[[NSArray alloc]init];
// Do any additional setup after loading the view, typically from a nib.
}
#pragma table View methods
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if (tableView == self.searchDisplayController.searchResultsTableView) {
return [self.searchresult count];
} else {
return [self.inventoryarray count];
}
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *simpleTableIdentifier = #"CellID";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableIdentifier];
}
if (tableView == self.searchDisplayController.searchResultsTableView) {
cell.textLabel.text = [self.searchresult objectAtIndex:indexPath.row];
} else {
cell.textLabel.text = [self.inventoryarray objectAtIndex:indexPath.row];
}
return cell;
}
#pragma search methods
-(void) filterContentForSearchText:(NSString *)searchText scope:(NSString *)scope
{
NSPredicate *resultPredicate = [NSPredicate predicateWithFormat:#"SELF contains[cd] %#", searchText];
self.searchresult = [self.inventoryarray filteredArrayUsingPredicate:resultPredicate];
}
-(BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString {
[self filterContentForSearchText:searchString scope:[[self.searchDisplayController.searchBar scopeButtonTitles] objectAtIndex:[self.searchDisplayController.searchBar selectedScopeButtonIndex]]] ;
return YES;
}
you forget to refesh data on table
-(void) filterContentForSearchText:(NSString *)searchText scope: (NSString *)scope
{
NSPredicate *resultPredicate = [NSPredicate
predicateWithFormat:#"SELF contains[cd] %#",
searchText];
self.searchresult = [self.inventoryarray filteredArrayUsingPredicate:resultPredicate];
// you forget to reload Data
[self.searchDisplayController.searchResultsTableView reloadData];
// else use
[yourtableView reloadData];
}
suggestion : UISearchDisplayController is deprecated, use UISearchController on onwards, for tutorial
You need to only [tableView reloadData]; in last of filterContentForSearchText method.
if you wan't to use UISearchController then...
- 'UISearchDisplayController' is deprecated: first deprecated in iOS 8.0 - UISearchDisplayController has been replaced with UISearchController
if you don't want to use UISearchController then use below method:
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText {} and reload tableview.
I am using searchdisplaycontroller,I have a table view above that i have a search bar... the table view displays a list of object names.. for example if i have an json object like
json :{
name: testname;
age:12;
maths:50;
english:56;
science:45;
},
{
// list of similar json objects
}
i have displayed only name in table row using cell.name.text = [json valueforKey :#"name"];
when i dont search anything i can get the whole object when ever user presses the any row by writing
[json objectAtIndex:indexPath.row];
My problem is when i search the table i am not getting the correct object at index... for example i search something in the search bar the it displays rows with relevant objects. when i use the same [json objectAtIndex:indexPath.row]; wrong index objects gets assigned pls help here is my code
-(void)filtercontentForSearchText:(NSString *)searchtext scope:(NSString *)scope{
NSPredicate *resultpredicate=[NSPredicate predicateWithFormat:#"SELF contains [cd] %#",searchtext];
searchList=[[searchDataArray valueForKey:#"SubjectDescription" ] filteredArrayUsingPredicate:resultpredicate];
NSLog(#"searchlist %#",searchList );
}
-(BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString{
[self filtercontentForSearchText:searchString scope:[[self.searchDisplayController.searchBar scopeButtonTitles ] objectAtIndex:[self.searchDisplayController.searchBar selectedScopeButtonIndex]]];
return YES;
}
//This is didselectrowAtindex code...
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"selected");
// if ([numberOfTouchesString isEqualToString:#"One"])
// {
NSArray *selected = [searchDataArray objectAtIndex:indexPath.row];
if (self.delegate && [self.delegate respondsToSelector:#selector(selectedSubject:)])
{
if (tableView == self.searchDisplayController.searchResultsTableView){
if ([self.searchDisplayController isActive]){
NSLog(#"searchlist %#",searchList);
indexPath = [self.searchDisplayController.searchResultsTableView indexPathForSelectedRow];
id searchResult = [searchList objectAtIndex:indexPath.row];
int indexForResult = [json indexOfObject:searchResult];
NSLog(#"indexpath%d " , indexForResult);
searchAppDelObj.didselectjsondata=[json objectAtIndex:indexPath.row];//storing that json data in AppDelegate object
_serachStr = [searchList objectAtIndex:indexPath.row];
//searchAppDelObj.valuePass=(NSArray *)_serachStr;
NSLog(#" searchAppDelObj.valuePass %#",(NSArray *)_serachStr);
//[self.delegate selectedSubject:self];
}
}
else
{
LearningSearchCell *cell = (LearningSearchCell *)[self.learningSearchTableView cellForRowAtIndexPath:indexPath];
NSString *cellText = cell.subjectNameLabel.text;
searchAppDelObj.didselectstring=cellText;
searchAppDelObj.didSelectArray=[searchDataArray objectAtIndex:indexPath.row];
NSLog(#"appobjarray %#",searchAppDelObj.didSelectArray);
NSLog(#"AppObj.didselectstring %#",searchAppDelObj.didselectstring);
//searchAppDelObj.valuePass=selected;
//[self.delegate selectedSubject:self];
}
if (-[_comparestr isEqualToString:#"search"]) {
searchAppDelObj.valuePass=(NSArray *)_serachStr;
NSLog(#" searchAppDelObj.valuePass %#",(NSArray *)_serachStr);
[self.delegate selectedSubject:self];
}else{
NSLog(#"AppObj.didselectstring %#",searchAppDelObj.didselectstring);
searchAppDelObj.valuePass=selected;
[self.delegate selectedSubject:self];
}
[self.revealViewController revealToggleAnimated:YES];
}
}
//Here is CellForRowAtindexpath code.........
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"learningSearchCellIdentifier";
LearningSearchCell *cell = [self.learningSearchTableView dequeueReusableCellWithIdentifier:cellIdentifier ];
if ((tableView==self.learningSearchTableView)==YES)
{
cell.subjectNameLabel.text=[[json objectAtIndex:indexPath.row]valueForKey:#"name"];
//subjectString=cell.subjectLabel.text;
cell.teacherNameLabel.text=[[json objectAtIndex:indexPath.row]valueForKey:#"age"];
cell.subjecDataAndSessionLabel.text=[[json objectAtIndex:indexPath.row]valueForKey:#"maths"];
NSNumber *test =[[searchDataArray objectAtIndex:indexPath.row]valueForKey:#"Cohort"];
NSString *myString =[NSString stringWithFormat:#"%#",test];
cell.chorotLabel.text=myString;
}
else if (tableView==self.searchDisplayController.searchResultsTableView) {
cell.subjectNameLabel.text=[searchList objectAtIndex:indexPath.row];
NSLog(#"indexpath %d",indexPath.row);
cell.teacherNameLabel.text=[[json objectAtIndex:indexPath.row]valueForKey:#"age"];
cell.subjecDataAndSessionLabel.text=[[json objectAtIndex:indexPath.row]valueForKey:#"maths"];
NSNumber *test =[[searchDataArray objectAtIndex:indexPath.row]valueForKey:#"english"];
NSString *myString =[NSString stringWithFormat:#"%#",test];
cell.chorotLabel.text=myString;
}
else{
cell.subjectNameLabel.text=[json objectAtIndex:indexPath.row];
}
return cell;
}
hope this will help you
As I understood that you want only those name which set of characters contain in your search bar,
first use NSMutableArray *nameJson to add your all the name from json.
and Use NSArray *nameShowing
_nameShowing = _nameJson;
and reload the tableview
uptill now you have your all your name in your table view , and now you search particular name
2. - (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
{
self.nameShowing = nil;
self.nameShowing = [[NSArray alloc]init];
NSPredicate *filter = [NSPredicate predicateWithFormat:#"SELF contains %#", self.searchbar.text];
self.nameShowing = [self.nameJson filteredArrayUsingPredicate:filter];
if(([self.searchbar.text isEqual:#""]))
{
self.nameShowing = self.nameJson;
}
[self.tblView reloadData];
}
I have a database with core data and i am trying to make it searchable. It doesn't work and returns this error:
'Can't do a substring operation with something that isn't a string
(lhs = (entity: Device; id: 0x8c63bc0
; data:
{.......}
So that is my code for creating cells.
- (NSManagedObjectContext *)managedObjectContext {
NSManagedObjectContext *context = nil;
id delegate = [[UIApplication sharedApplication] delegate];
if ([delegate performSelector:#selector(managedObjectContext)]) {
context = [delegate managedObjectContext];
}
return context;
}
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
self.searchResults = [[NSArray alloc]init];
[super viewDidLoad];
// Uncomment the following line to preserve selection between presentations.
// self.clearsSelectionOnViewWillAppear = NO;
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem;
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
// Fetch the devices from persistent data store
NSManagedObjectContext *managedObjectContext = [self managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:#"Device"];
self.devices = [[managedObjectContext executeFetchRequest:fetchRequest error:nil] mutableCopy];
[self.tableView reloadData];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (tableView == self.searchDisplayController.searchResultsTableView) {
return [self.searchResults count];
}
else
{
return [self.devices count];
}
// Return the number of rows in the section.
// return self.devices.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
if (tableView == self.searchDisplayController.searchResultsTableView) {
NSManagedObject *device = [self.searchResults objectAtIndex:indexPath.row];
[cell.textLabel setText:[NSString stringWithFormat:#"%#", [device valueForKey:#"text1"]]];
[cell.detailTextLabel setText:[device valueForKey:#"text3"]];
}
else
{
// Configure the cell...
NSManagedObject *device = [self.devices objectAtIndex:indexPath.row];
[cell.textLabel setText:[NSString stringWithFormat:#"%#", [device valueForKey:#"text1"]]];
[cell.detailTextLabel setText:[device valueForKey:#"text3"]];
}
return cell;
}
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
// Return NO if you do not want the specified item to be editable.
return YES;
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
NSManagedObjectContext *context = [self managedObjectContext];
if (editingStyle == UITableViewCellEditingStyleDelete) {
// Delete object from database
[context deleteObject:[self.devices objectAtIndex:indexPath.row]];
NSError *error = nil;
if (![context save:&error]) {
NSLog(#"Can't Delete! %# %#", error, [error localizedDescription]);
return;
}
// Remove device from table view
[self.devices removeObjectAtIndex:indexPath.row];
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
}
#pragma mark - Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// Navigation logic may go here. Create and push another view controller.
/*
<#DetailViewController#> *detailViewController = [[<#DetailViewController#> alloc] initWithNibName:#"<#Nib name#>" bundle:nil];
// ...
// Pass the selected object to the new view controller.
[self.navigationController pushViewController:detailViewController animated:YES];
*/
}
#pragma mark - Segue
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"UpdateJob"]) {
NSManagedObject *selectedDevice = [self.devices objectAtIndex:[[self.tableView indexPathForSelectedRow] row]];
JobDetailViewController *destViewController = segue.destinationViewController;
destViewController.device = selectedDevice;
}
}
and the code for the query:
#pragma Search Methods
-(void)filterContentForSearchText:(NSString *)searchText scope:(NSString *)scope
{
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF beginswith[c] %#",searchText];
self.searchResults = [self.devices filteredArrayUsingPredicate:predicate];
}
-(BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString
{
[self filterContentForSearchText:searchString scope:[[self.searchDisplayController.searchBar scopeButtonTitles] objectAtIndex:[self.searchDisplayController.searchBar selectedScopeButtonIndex]]];
return YES;
}
but it keeps breaking when i enter the first letter. What can i do?
Not a predicate expert but shouldnt SELF beginswith[c] %# be text1 beginswith[c] %# since the predicate is used on an array of object Device
I've solved it. I've added this line of code.
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
// More initializations if needed.
}
just below
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
Thanks for all the help guys!
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];
}
}
I'm using single NSFetchedResultController to populate both self.tableView and UISearchDisplayControler.tableView. After using search bar and dismissing it if I select any row in the self.tableView I get this error:
Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayM objectAtIndex:]: index 0 beyond bounds for empty array'
I guess that somewhere I miss reloading data but I have no clue where?
Here are some methods which I use for populating both tabble views:
#pragma mark - Table View
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Count sections in FRC
return [[self.groupsFRC sections] count];
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Count groups in each section of FRC
return [[[self.groupsFRC sections] objectAtIndex:section] numberOfObjects];
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// Get reusable cell
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"GroupCell"];
// If there isn't any create new one
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"GroupCell"];
}
Group *group = [self.groupsFRC objectAtIndexPath:indexPath];
cell.textLabel.text = group.name;
// Checking if group has been selected earlier
if ([self.selectedGroups containsObject:#{#"name" : group.name, #"id" : group.cashID}]) {
[cell setAccessoryType:UITableViewCellAccessoryCheckmark];
} else {
[cell setAccessoryType:UITableViewCellAccessoryNone];
}
return cell;
}
// Adding checkmark to selected cell
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *selectedCell = [tableView cellForRowAtIndexPath:indexPath];
Group *group = [self.groupsFRC objectAtIndexPath:indexPath];
// Checking if selected cell has accessory view set to checkmark and add group to selected groups array
if (selectedCell.accessoryType == UITableViewCellAccessoryNone)
{
selectedCell.accessoryType = UITableViewCellAccessoryCheckmark;
[self.selectedGroups addObject:#{#"name" : group.name, #"id" : group.cashID}];
NSLog(#"%#", self.selectedGroups);
}
else if (selectedCell.accessoryType == UITableViewCellAccessoryCheckmark)
{
selectedCell.accessoryType = UITableViewCellAccessoryNone;
[self.selectedGroups removeObject:#{#"name" : group.name, #"id" : group.cashID}];
NSLog(#"%#", self.selectedGroups);
}
// Hiding selection with animation for nice and clean effect
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}
#pragma mark - Filtering
- (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope
{
NSPredicate *resultPredicate = [NSPredicate predicateWithFormat:#"SELF.name contains[c] %#", searchText];
self.groupsFRC = [Group MR_fetchAllSortedBy:#"caseInsensitiveName"
ascending:YES
withPredicate:resultPredicate
groupBy:#"sectionLetter"
delegate:self];
}
-(BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString
{
[self filterContentForSearchText:searchString
scope:[[self.searchDisplayController.searchBar scopeButtonTitles] objectAtIndex:[self.searchDisplayController.searchBar selectedScopeButtonIndex]]];
return YES;
}
-(void)searchDisplayController:(UISearchDisplayController *)controller didHideSearchResultsTableView:(UITableView *)tableView
{
// Setting FRC to fetch original table view data
self.groupsFRC = [Group MR_fetchAllSortedBy:#"caseInsensitiveName"
ascending:YES
withPredicate:nil
groupBy:#"sectionLetter"
delegate:self];
}
Okey, I finally get over this problem but with skipping UISearchDisplayDelegate and implementing this only using UISearchBarDelegate. Here is the code:
-(void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
{
if (searchText.length > 0) {
resultPredicate = [NSPredicate predicateWithFormat:#"SELF.name contains[c] %#", searchText];
} else {
resultPredicate = nil;
}
[self refreshFRC];
}
-(void)searchBarCancelButtonClicked:(UISearchBar *)searchBar
{
resultPredicate = nil;
[self refreshFRC];
}
- (void)refreshFRC {
if (resultPredicate != nil) {
self.groupsFRC = [Group MR_fetchAllSortedBy:#"caseInsensitiveName"
ascending:YES
withPredicate:resultPredicate
groupBy:#"sectionLetter"
delegate:self];
} else {
self.groupsFRC = [Group MR_fetchAllSortedBy:#"caseInsensitiveName"
ascending:YES
withPredicate:nil
groupBy:#"sectionLetter"
delegate:self];
[self.tableView reloadData];
}
}