I have a plist (an array of dictionary's) that I am reading into an NSArray which I am using to populate a table. It's a list of people and their work location, phone number, etc. I added a UISearchBar and implemented the textDidChange method as well.
When I search by the person's last name I do see the filtered list in the table, however I don't think that I am storing the filtered results properly. I am using an NSMutable Array but I am losing the key:value pairing.
Can someone please point me in the right direction? I ultimately would like to click on a filtered name and push to a detailed view controller. I believe my issue is that I am trying to capture the filtered results in an NSMutableArray but I am not certain.
I've done a lot of Googling but can't seem to put this together in my head. Any help is appreciated.
Kind Regards,
Darin
Here is the array that I am using to load the plist.
-(NSArray *)content
{
if (!content){ {
content = [[NSArray alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"ophonebook" ofType:#"plist"]];
NSSortDescriptor* nameSortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"Last" ascending:YES];
content= [content sortedArrayUsingDescriptors:[NSArray arrayWithObject:nameSortDescriptor]];
}
return content;
}
Here is the UISearchBar Method
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText {
if (searchText.length == 0)
{
isFiltered= NO;
} else {
isFiltered= YES;
filteredPeople = [[NSMutableArray alloc]init];
for (NSDictionary *dict in content)
{
NSString *last = [dict objectForKey:#"Last"];
NSRange lastRange = [last rangeOfString:searchText options:NSCaseInsensitiveSearch];
if (lastRange.location != NSNotFound)
{
[filteredPeople addObject: [dict objectForKey:#"Last"]];
}
}
}
[myTableView reloadData];
}
Keep things simple, get rid of the flags and the different arrays in your table delegate methods. Instead, have 1 array for your source data (content) and another array for your source data that is actually for display (call it displayContent).
Now, when you start up, set displayContent = content.
Then, when you want to filter, set displayContent = [content filteredArrayUsingPredicate:...]; (you can convert your current loop into a simple predicate).
Finally, when you're done searching, set displayContent = content.
No flags. No ifs in the table delegate methods. Simple, readable code.
p.s. your problem is:
[filteredPeople addObject: [dict objectForKey:#"Last"]];
which you should be setting to:
[filteredPeople addObject:dict];
so you have all the data instead of just the names. Though technically you could still make it work by searching for the last name in your content.
Related
In my project i get response data in the form of
{
"procedures": [5950]
0: {
"Procedures": {
"id": "1"
"procedure_name": "3d render w/o postprocess"
"procedure_code": "76376"
}
}
1: {
"Procedures": {
"id": "2"
"procedure_name": "3d rendering w/postprocess"
"procedure_code": "76377"
}
there are 5950 elements in the data array. i create separate arrays for "id" and "Procedure_name" and show data in UITableView it displays correct data with id.
I also applied search functionality to filter data as it is so difficult to find any element in 5950 elements by scrolling.
search function is also works well but when i select any UITableViewCell of the filtered result it doesn't provide the actual id of that element whereas it returns the current indexpath value of the UITableView.
for searching i put the following code
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
tempArray = [NSArray arrayWithArray:dataArray];
NSString *stringToSearch = textField.text;
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF BEGINSWITH[c] %#",stringToSearch]; // if you need case sensitive search avoid '[c]' in the predicate
NSArray *tempresults = [dataArray filteredArrayUsingPredicate:predicate];
if (tempresults.count > 0)
{
tempArray = [NSArray arrayWithArray:tempresults];
}
[searchdiagtable reloadData];
return YES;
}
in DidSelectRow method i apply the following code
if (tableView == searchdiagtable)
{
UITableViewCell *selectedCell = [searchdiagtable cellForRowAtIndexPath:indexPath];
NSLog(#"%#", selectedCell.textLabel.text);
searchdiag. text = selectedCell.textLabel.text;
searchResultId = [iddict objectAtIndex:indexPath.row]; // iddict is the array where i store the values of "id" from dictionary.
[searchdiagtable deselectRowAtIndexPath:indexPath animated:YES];
searchdiagtable.hidden = YES;
}
checking on your question which I can get is issue must be using a wrong array iddict for search as it looks to me its should be tempArray
searchResultId = [iddict objectAtIndex:indexPath.row];
This line of code creates the problem, tableView reuses cells.
if you selected the cell , it selects the cell index (0, 1, 2 ... etc). So your array returns the first values.
If you are not displaying the "Id" in your table view. You cant get correct id from your array.
There is one solution for this,save id and name in a single dictionary,
like:{
"id":"name"
}
using the name you can get the id from dictionary.
hey you already get two arrays , one for ids and one for names.
How you do that..after response may be you did like this
NSMutableArray *id;
NSMutableArray *name;
for( ------ ){
[id addObject:[dict objectforkey:#"id"];
[name addObjec:[dict objectForKey:#"procedure_name"];
}
dict is reponse dictionary.
This is the way how you get arrays.now for dictionary
NSMutableDictionary *dict1 = [[NSMutableDictionary alloc]init];
for(){
[dict setObject:[dict objectforkey:#"id"] forKey:[dict objectForKey:#"procedure_name"];
}
just check the for loop carefully in your code..
i just get an easy way to create A:B type dictionary
i am sharing my code i used and works perfectly for me
NSDictionary * searchdict = [NSDictionary dictionaryWithObjects:dataArray forKeys:iddict];
NSLog(#"dict formed by combining %#",searchdict);
where dataarray and iddict are two arrays
it gives output dictionary as
1034 = "Catheterize for urine spec";
1035 = "Cauterization of cervix";
1036 = "CBC without platelet";
1037 = "CBC/diffwbc w/o platelet";
1038 = "Cbt 1st hour";
1039 = "Cbt each addl hour";
104 = "Amino acids quan 6 or more";
1040 = "Ccp antibody"; ......
yaa, I know we can directly add two arrays to dictionary. But it takes memory for two arrays and dictionary also.. so instead of creating two arrays.you can directly add it to dictionary. But what you did is also correct. In mobile applications every time we have to think about memory management also..
I have an array of NSDictionaries and i can access the values in them just fine but i am trying to filter these dictionaries down based on a user's search (user can only search by the dictionary key (#"uniqueSignName").
Once the user has searched through the names property i then need to display ALL dictionary associated data for that #"uniqueSignName" value.
I do the following code and always get the correct amount of NSLogs. For the life of me i cannot remember how to GET those dictionaries.
for (int i = 0; i < [filteredDictionaries count]; i++) {
if ([[[filteredDictionaries valueForKey:#"uniqueSignName"] objectAtIndex:i] isEqualToString:[self.filteredResults objectAtIndex:indexPath.row]]) {
NSLog(#"Power Rangers");
}
}
Eg: I search for "John"
NSLog: #"Power Rangers"
Correctly only appears once.
Now, how do i access another property of "John's" dictionary?
If you want to search the name then better way is that to use NPredicate without iterating the array.
Please see the below example..it may help you...
// Here array is your main array...
NSArray *filteredarray = [array filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"(uniqueSignName == %#)", #"John"]];
So the problem is solved, I can now access all the filtered properties.
for (NSDictionary *dict in filteredDictionaries) {
if ([[self.filteredResults objectAtIndex:indexPath.row] isEqualToString: dict[#"uniqueSignName"]]) {
NSString *myString = [NSString stringWithFormat:#"%#", dict[#"pType"]];
NSLog(#"hugh: %#", myString);
myString = displayPtype;
}
}
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];
}
I'm sure this question has been asked before, but I couldn't find anything in the search.
I have a list of city names which can change depending on the country selected. I would like to order these alpabetically and have them show up in sections based on the first letter (like the contacts list).
How can I accomplish this?? Do I have to manually create all the sections or is there a built-in way?
Thanks.
Sort your datasource to be in the order you wish, then reload the tableview.
Try making an NSDictionary of NSMutableArrays. Take the first letter of each city, toUpper it. Then insert your city into the array for the letter k.
Number of sections = [[NSDictionary allKeys] count]
NSArray* cityNames = [NSArray arrayWithObject: #"Detroit"];
NSMutableDictionary* dataSource = [[NSMutableDictionary alloc] init];
for( NSString* city_name in cityNames){
NSString* key = [[city_name substringToIndex:1] uppercaseString];
if( ![dataSource objectForKey: key] ){
[dataSource setObject:[NSMutableArray array] forKey:key];
}
[[dataSource objectForKey: key] addObject: city_name];
}
If you need more help setting up your datasource with your tableview say so in a comment and I'll help you more. Cheers
Yes, you have to manually create all sections. And it would be convenient to store sections in NSDictionary. To have lettered section indicies just return an array of them in sectionIndexTitlesForTableView:
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