display search result in table view in objective-c - ios

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.

Related

Parse SDK for iOS; Using UISearchBar on UITableView

I'm retrieving data from parse and put it the tableview but when I'm trying to also add a searchbar I don't know how can I manage it. What should I do on tableView:cellForRowAtIndexPath: method instead of NSLog(#"do something!!");
My code is below and thanks for any help.
retrieve from parse code is below
- (void) retrieveFromParse {
PFQuery *retrieveName = [PFQuery queryWithClassName:#"pFihrist"];
[retrieveName findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
nameArray = [[NSMutableArray alloc] initWithArray:objects];
}
[tableView reloadData];
}];
}
UITableView Delegate code is below
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (self.tableView == self.searchDisplayController.searchResultsTableView) {
return [searchResults count];
} else {
return [nameArray count];
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"fCell";
FihristCell *cell = (FihristCell *)[self.tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[FihristCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
if (tableView == self.tableView) {
PFObject *tempObject = [nameArray objectAtIndex:indexPath.row];
cell.cellProfName.text = [tempObject objectForKey:#"pName"];
} else {
NSLog(#"do something!!");
}
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"cell tapped");
}
search methods code is below
- (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope
{
NSPredicate *resultPredicate = [NSPredicate predicateWithFormat:#"name contains[c] %#", searchText];
searchResults = [nameArray filteredArrayUsingPredicate:resultPredicate];
}
-(BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString
{
[self filterContentForSearchText:searchString
scope:[[self.searchDisplayController.searchBar scopeButtonTitles]
objectAtIndex:[self.searchDisplayController.searchBar
selectedScopeButtonIndex]]];
return YES;
}
First Register the Cell to display you searched items
You have to register the cell to searchdisplaycontroller as well.
Important: You must register a class or nib file using the registerNib:forCellReuseIdentifier: or registerClass:forCellReuseIdentifier: method before calling this method.
in View did load
If you are Creating cell in code
[self.searchDisplayController.searchResultsTableView registerClass:[TableViewCell class]
forCellReuseIdentifier:#"IdentifierForCell"];
If you are Creating cell in nib
[self.searchDisplayController.searchResultsTableView registerNib:[UINib nibWithNibName:#"CellNibName" bundle:nil]
forCellWithReuseIdentifier:#"IdentifierForCell"];
2. Deque corresponding cell to search display controller table with identifier
FihristCell *cell = (FihristCell *)[self.searchDisplayController.searchResultsTableView dequeueReusableCellWithIdentifier:CellIdentifier];
3. configure that cell to display instead of NSLog

Terminating App due to'NSInvalidArgumentException'

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.

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];
}
}

Repopulating UITableView after search

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];
}
}

UISearchBar error - NSRangeException

I'm getting an error trying to use a UISearchBar to filter objects from an array that populate a UITableView. The error states: Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayM objectAtIndex:]: index 3 beyond bounds [0 .. 2]'.
The relevant code in ViewController.m is:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
if (isFiltered == YES)
{
NSLog (#"%i", indexPath.row);
cell.textLabel.text = [filteredCities objectAtIndex:indexPath.row];
}
else
{
cell.textLabel.text = [initialCities objectAtIndex:indexPath.row];
}
return cell;
}
#pragma mark - UISearchBar Delegate
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
{
if (searchText.length == 0)
{
isFiltered = NO;
}
else
{
isFiltered = YES;
filteredCities = [[NSMutableArray alloc] init];
for (NSString *cityName in initialCities)
{
NSRange cityNameRange = [cityName rangeOfString:searchText options:NSCaseInsensitiveSearch];
if (cityNameRange.location != NSNotFound)
{
[filteredCities addObject:cityName];
}
}
}
[myTableView reloadData];
}
#end
You should also change tableView:numberOfRowsInSection method to something like this:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if (isFiltered) {
return [filteredCities count]; /// Filtered items
}
return [initialCities count]; /// All items
}
there is a better way to do this. you should use a search bar controller. and use these two delegate methods.
- (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope {
NSPredicate *resultPredicate = [NSPredicate
predicateWithFormat:#"SELF.property contains[cd] %#",
searchText];
self.searchResults = [self.datasource filteredArrayUsingPredicate:resultPredicate];
}
// code to refresh the table with the users search criteria
// this gets called everytime the user search string changes.
// also it calls the function above.
-(BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString {
[self filterContentForSearchText:searchString
scope:[[self.searchDisplayController.searchBar scopeButtonTitles]
objectAtIndex:[self.searchDisplayController.searchBar
selectedScopeButtonIndex]]];
return YES;
}
Now in your table view. you can refer to the new table the search bar controller creates by saying
self.searchBarController.searchResultsTable
and you can put this in the if else and check to see the table rather than using an isFiltered property.
if (tableView == self.searchDisplayController.searchResultsTableView){
} else { // your in the other original table

Resources