Search bar in core data project - uitableview

I use core data with magical record and i'm try to filter data with a search bar in a table view.
I write two methods to get the number of rows and the name of the cells:
-(int) dammiNumeroCercati:(NSString *)searchBar
{
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"nome CONTAINS [cd] %#", searchBar];
NSArray*arra = [Ricetta MR_findAllSortedBy:#"nome" ascending:YES withPredicate:predicate];
return arra.count;
}
-(NSString*) dammiNomeRicettaCercata:(NSString *)searchBar mostrataNellaCella: (int) cella
{
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"nome CONTAINS [cd] %#", searchBar];
NSArray *arra = [Ricetta MR_findAllSortedBy:#"nome" ascending:YES withPredicate:predicate];
Ricetta*ctn = arra[cella];
return [NSString stringWithFormat:#"%#", ctn.nome];
}
then i call this method inside the numberOfRowsInSection: and cellForRowAtIndexPath: inside an if cycle:
if (self.mySearchBar.isFirstResponder){
// the above methods
} else {
// the normals methods to have all the data
}
somebody know where I'm wrong or if I miss somethings?

searchBar is usually a UISearchBar, not a string.
You should use searchBar.text and process that in your methods.
Also, in your table view's datasource methods you have to make sure which table view is causing the callback, and then return the correct count/string. Usually this is checked by comparing pointers to the two tables (original table view and search results table view).
-(NSUInteger)tableView:(UITableView*)tableView
numberOfRowsInSection:(NSUInteger)section {
if (tableView == _tableView) {
// return the usual row count
}
return [self dammiNumeroCercati:_searchBar.text];
}

Related

How to present an empty cell?

My task is simple basically for most of you, but obviously im doing something wrong here.
I have a two table view controllers that I want to populate their cells based on some object status. Im working with core data so also doing fetchResultController too.
in one of the view controllers I can change the status of the cell object (not the actual cell, the object that is populating the cell), and whenever that happened I want to delete this cell from the table view. In that view controller I want to present only the cells that the object that is populating them have the status = 0. but I dont want to delete the ones that was deleted, just to change their status to 1, and then in some other view controller to present them if they have the status = 1.
So what im doing is:
Whenever the commitEditingStyle is getting called:
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
Target *target = [self.fetchedResultController objectAtIndexPath:indexPath];
target.status = #1;
}
if ([_delegate respondsToSelector:#selector(didDeleteObject)]) {
[self fetchData];
[_delegate didDeleteObject];
}
}
Now what I had in mind is in the cellForRowAtIndexPath method to add a condition when im returning a cell like this:
if ([target.status isEqual:#0]) {
return cell;
}
return NULL;
}
since I want to present only the cells that have the status 0...
So now obviously I cannot enter this table view because I have to return a cell in this method and not done, how can i just say that if the statues of the object target is different than 1 so dont present the cell?
other code solutions will be great too :)
thanks!!!
You should be able to filter out fetch request with a predicate
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:#"Target"];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"status == %#", [NSNumber numberWithBool:NO]];
[fetchRequest setPredicate:predicate];
When you set your status attribute to 1, FetchedResultsController will automatically delete cell from the tableView, but the object will not get deleted. You don't have to do anything in the cellForRowAtIndexPath. And in your other tableView, set the predicate to only fetch status == 1.
Also, make sure to save the managedObjectContext when you change the attribute's value.
Target *target = [self.fetchedResultController objectAtIndexPath:indexPath];
target.status = #1;
NSError *error = nil;
if (![self.managedObjectContext save:&error]) {
NSLog(#"Save Failed! %# %#", error, [error localizedDescription]);
}
You can't do it in this way.
You have to set a dynamic numberOfRowsInSection:, depending on how many Target has status property equal to #0
So, in tableView:cellForRowAtIndexPath: you will return the cell only if the status is #0.
For me is better if you split the dataSource in dataSource with target.status = #0 and others.
A right way is this
// This is the point where you have an update dataSource array (not splitted)
updatedDatas = [Manager getLatestData];
if (dataSource.count > 0)
[dataSource removeAllObjects];
[updatedDatas enumerateObjectsUsingBlock:^(Target *obj, NSUInteger idx, BOOL *stop) {
if ([obj.status integerValue] == 0)
[dataSource addObject: obj];
}];
updatedDatas = nil;
// This is the numberOfRowsInSection:
- (NSInterger)numberOfRowsInSection:(NSInteger)section{
return dataSource.count;
}
In the tableView:cellForRowAtIndexPath: you will now use the dataSource NSMutableArray with a simple objectAtIndex: to access the correct element

Adding a Search Bar to a Table View with Core Data

I have an app that saves data to a Core Data sql then view it in a table view.
The data model:
Entity: Fruits
Attributes: name, picture
So in the table view, cell.textLabel.text = Fruits.name
Each cell has a segue with a view that shows Fruits.picture.
I want to add a search bar to search in Fruits names.
I followed this tutorial: http://www.appcoda.com/search-bar-tutorial-ios7/
But the problem I had is in filterContentForSearchText
- (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope
{
NSPredicate *resultPredicate = [NSPredicate predicateWithFormat:#"name contains[c] %#", searchText];
searchResults = [recipes filteredArrayUsingPredicate:resultPredicate];
}
I'm using core data not an array for storing data. And I don't know how to filter it so I can display searchResults in table cells and use it in prepareForSegue.
Since you're using core data and not an array of Fruits you should filter your data this way:
- (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope
{
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription
entityForName:#"Fruits" inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:entity];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"name contains[c] %#", searchText];
[fetchRequest setPredicate:predicate];
NSError *error;
NSArray* searchResults = [managedObjectContext executeFetchRequest:fetchRequest error:&error];
}
To show the picture in a second viewController you should put something like this in you prepareForSegue method:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
Fruit *selectedItem;
if (self.searchDisplayController.active) {
selectedItem = [searchResults objectAtIndex:indexPath.row];
} else {
selectedItem = [fruitsList objectAtIndex:indexPath.row];
}
DestinationViewController *destination = segue.destinationViewController;
destination.fruit = selectedItem;
}
Where fruitsList is an array with all the fruit objects (The one used to show the fruits without any filter), and DestinationViewController is the the controller that will show the picture.
First, why is your entity name in the plural? That is confusing. You should rename your entity Fruit.
Second, how come your variable names start with capital letters? This is again confusing because they can be mistaken for class names. You should rename your variables fruit, such as in fruit.name.
Third, you should use a NSFetchedResultsController to populate your table view. You could have a separate one just for the search or filter in memory. Both are preferable to doing a manual fetch in each call to filterContentForSearchText. Your filter code looks fine (it is the in-memory version). However, I do not see where you reload the data of your search results table view.
Fourth, the search is completely unrelated to the segue problem. Each cell should be associated with a particular Fruit instance. (Either custom cells with a #property of type Fruit, or use the fetched results controller objectAtIndexPath.) In prepareForSegue you just assign that fruit to the detail view controller (which should have an appropriate #property set up).

How to search cell in UITableView from multiple array in IOS

Actually am getting datas from JSON webservice. I need to search data from UITableView cells using uisearchdisplaycontroller.
I have passed data into cells successfully using NSMutablArray with multiple array. But now i need to search data from that.
My array:
[
{
"name": "jhon",
"city": "chennai",
}
{
"name": "Micle",
"city": "Newyork",
}
{
"name": "Micle",
"city": "Washigton",
}
]
My custom cells in UITableView:
cell.P_name.text = [details objectForKey:#"name"];
cell.P_city.text = [details objectForKey:#"city"];
In Search i tried :
- (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope
{
//-- Help to customize operations
searchResults = [[NSArray alloc]initWithArray:mainArray];
NSPredicate *resultPredicate = [NSPredicate predicateWithFormat:#"SELF contains[cd] %#", searchText];
searchResults = [details filteredArrayUsingPredicate:resultPredicate];
[tableView reloadData];
}
Can anybody help to resolve my issue.
You should have 2 arrays:
Your main store of data from your JSON (mainDataList)
Your data source array used to populate your table view(s) (dataSourceList)
Initially:
self.dataSourceList = self.mainDataList;
because you are displaying all of the data in your table view. When any search is cancelled you also go back to this state (then reload).
When searching however, you need to filter the contents of the dataSourceList and reload the table:
- (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope
{
NSPredicate *resultPredicate = [NSPredicate predicateWithFormat:#"name contains[cd] %#", searchText];
self.dataSourceList = [self.mainDataList filteredArrayUsingPredicate:resultPredicate];
[tableView reloadData];
}
NOTE: the above predicate only searches the name in your dictionaries...
Now, all of your table view methods only use self.dataSourceList, and you modify its contents based on your state. You code is clean and simple. Smile :-)
I just put my logic take one more mutable array in .h file name is _temArray
And add your _mainArray (populated on tableView) to your _temArray such like,
[_temArray addObjectsFromArray:_mainArray];
In Search method use NSPredicate:
- (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope
{
//-- Help to customize operations
if([searchText length] > 0)
{
NSMutableArray *fileterMainArray = (NSMutableArray *)_temArray
NSPredicate *predicatePeople = [NSPredicate predicateWithFormat:#"name BEGINSWITH[cd] %#", searchText]; // here if you want to search by city then change "name" to "city" at pattern.
NSArray *filteredArray = [fileterMainArray filteredArrayUsingPredicate:predicatePeople];
[_mainArray addObjectsFromArray:filteredArray];
}
else
[_mainArray addObjectsFromArray:_temArray];
[self.tblView reloadData]; // don't forget to reload Table data.
}

Understanding how to use UISearchBar with Core Data

I have an iPad app (Xcode 4.6, iOS 6.2, ARC and Storyboards). I have a UITableView that contains prototype cells, with two labels (lName and lPhone). I have filled a NSArray with the results of the Core Data store. I copied the code from a sample, and am lost! I have two fields I am looking for: name and phone number. I want to be able to search on either one. I tried using the UISearchBar Controller, but the results span the entire window, which is not acceptable. So, I'm trying to do this without the controller. I want the search to filter the shown entries in the UITableView, which this bit of code is supposed to do.
When I do the MR_findAll (MagicalRecord), I get all of the attributes in the Core Data store. This is where I'm lost - how do I get the two attributes out of the array and into the NSMutableArray allTableData, or is it even necessary in this case?
This is my code, so far:
NSArray *allDataArray = [ClientInfo MR_findAll];
// move objects from Core Data store to NSMutablearray
[allTableData addObjectsFromArray:allDataArray];
if(text.length == 0) {
isFiltered = FALSE;
}
else {
isFiltered = true;
filteredTableData = [[NSMutableArray alloc] init];
for (ClientCell* client in allTableData) {
NSRange nameRange = [client.lName.text rangeOfString:text options:NSCaseInsensitiveSearch];
NSRange phoneRange = [client.lPhone.text rangeOfString:text options:NSCaseInsensitiveSearch];
if(nameRange.location != NSNotFound || phoneRange.location != NSNotFound) {
[filteredTableData addObject:client];
}
}
}
I also don't understand how the NSRange is going to match against the two fields I'm looking for. I'm really confused here.
The rangeOfString method returns an NSRange with a location that's not equal to NSNotFound when a given substring is found in the receiver string. What your code does is that it first checks the range of the search text in client.lName.text and client.lPhone.text. Next, the code adds the object to filteredTableData if either of the ranges exist.
As for adding only your two attributes to the filteredTableData, this is simply not needed, as you should access the already stored object to fetch these attributes.
Finally, I'd also recommend you have a look at the free Sensible TableView framework as it should help you perform these kind of searches automatically.
You have to Have to do some thing like this
Fetch the Data from coredata into an array which is pretty mandatory and you have lot of tutorials on that.
And then in the search bar delegate method do implement some thing like this. Which will start filtering your array so that you can see your desired results
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar
{
self.tableView.allowsSelection = YES;
self.tableView.scrollEnabled = YES;
NSArray *list = [[NSArray alloc] initWithArray:artists];
if (searchBar.text && [searchBar.text length] > 0)
{
NSMutableArray *filterContacts = [[NSMutableArray alloc]initWithArray:list];
// NSPredicate *predicate = [NSPredicate predicateWithFormat:#"title LIKE %#",searchBar.text ];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"title CONTAINS[c] %#",searchBar.text ];
[filterContacts filterUsingPredicate:predicate];
artists = filterContacts;
}
[self.tableView reloadData];
[self updateSearchString:searchBar.text];
}

Search NSArray of NSDictionary (which contains NSArray of NSDictionary, repeatedly)

I have a data-structure (in plist) that looks something like this:
What i have here is an NSArray of NSDictionary. Each NSDictionary has two keys:
Title
Link (recursive)
This forms a tree like structure, with variable length branches i.e. some branches can die at level 0, and some can be as large as level 3 or more.
I'm showing this structure in UITableView (with a little help from UINavigationController). This was easy enough.
Note: On tapping the Leaf Node
(represented by NSDictionary object
with Nil or Zero as "Link"), an
event is triggered i.e. Model window
appears with some information.
Now, i need to add Search support.
Search bar will appear above UITabeView (for Level 0). I need to come-up with a way to search this tree like structure, and then show the results using UISearchDisplayController, and then allow users to navigate the results as well.
How?... is where i'm a little stuck
and need some advise.
The search has to be quick, because we want search as you type.
p.s. I've thought of translating this data structure to CoreData, and it's still lurking in my mind. If you think it can help in this case, please advise.
Edit:
Here's my current solution, which is working (by the way):
#pragma mark -
#pragma mark UISearchDisplayController methods
- (void)searchBarResultsListButtonClicked:(UISearchBar *)searchBar {
NSLog(#"%s", __FUNCTION__);
}
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString {
NSLog(#"%s", __FUNCTION__);
[self filterCategoriesForSearchText:searchString
scope:[controller.searchBar selectedScopeButtonIndex]];
// Return YES to cause the search result table view to be reloaded.
return YES;
}
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchScope:(NSInteger)searchOption {
NSLog(#"%s", __FUNCTION__);
[self filterCategoriesForSearchText:[controller.searchBar text]
scope:[controller.searchBar selectedScopeButtonIndex]];
// Return YES to cause the search result table view to be reloaded.
return YES;
}
#pragma mark UISearchDisplayController helper methods
- (void)filterCategoriesForSearchText:(NSString *)searchText scope:(NSInteger)scope {
self.filteredCategories = [self filterCategoriesInArray:_categories forSearchText:searchText];
NSSortDescriptor *descriptor = [[[NSSortDescriptor alloc] initWithKey:KEY_DICTIONARY_TITLE ascending:YES] autorelease];
[self.filteredCategories sortUsingDescriptors:[NSArray arrayWithObjects:descriptor, nil]];
}
- (NSMutableArray *)filterCategoriesInArray:(NSArray *)array forSearchText:(NSString *)searchText {
NSMutableArray *resultArray = [NSMutableArray array];
NSArray *filteredResults = nil;
// Apply filter to array
// For some weird reason this is not working. Any guesses? [NSPredicate predicateWithFormat:#"%# CONTAINS[cd] %#", KEY_DICTIONARY_TITLE, searchText];
NSPredicate *filter = [NSPredicate predicateWithFormat:#"Title CONTAINS[cd] %#", searchText];
filteredResults = [array filteredArrayUsingPredicate:filter];
// Store the filtered results (1)
if ((filteredResults != nil) && ([filteredResults count] > 0)) {
[resultArray addObjectsFromArray:filteredResults];
}
// Loop on related records to find the matching results
for (NSDictionary *dictionayObject in array) {
NSArray *innerCategories = [dictionayObject objectForKey:KEY_DICTIONARY_LINK];
if ((innerCategories != nil) && ([innerCategories count] > 0)) {
filteredResults = [self filterCategoriesInArray:innerCategories forSearchText:searchText];
// Store the filtered results (2)
if ((filteredResults != nil) && ([filteredResults count] > 0)) {
[resultArray addObjectsFromArray:filteredResults];
}
}
}
return resultArray;
}
Core Data would be able to perform the search in the data store pretty efficiently, and would scale the search to more levels efficiently. Also, if you use NSFetchedResultsController for the TableView it would almost certainly be more memory efficient - the worst case would only have one level array loaded at any given time. And the best case is considerably better, as it would only have faulted a few objects into the array. HTH

Resources