How to implement searchbar if the tableview is loaded from a dictionary? - iphone

I have a table view and adding items to that like this.`
NSDictionary *memberInfo = [self.currentChannel infoForMemberWithID:memberID];
memberinfo=memberInfo;
cell.textLabel.text = [NSString stringWithFormat:#"%#",[memberinfo objectForKey:#"name"]];
cell.detailTextLabel.text = [NSString stringWithFormat:#"%#",[memberinfo objectForKey:#"status"]];
cell.textLabel.backgroundColor = [UIColor clearColor];
cell.detailTextLabel.backgroundColor = [UIColor clearColor];
return cell;
`and it was working fine.Now i want to add a searchbar on to that.i need to load the tableview according to the matched string.But i am loading the tableview like this.I know how to search in an array.I am using
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText.i want to load the table view according to the match of the searchstring ,Can anybody know this?

You can use NSPredicate inside your SearchBar delegate to filter your main array (members or whatever you named it) and catch the results in an another global declared array. Now reload your table with this filtered array.
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
{
[_mArrFilteredList removeAllObjects]; //global array which will contain filtered results
NSString* searchStr = [NSString stringWithFormat:#"*%#*",_srbActivity.text];
NSPredicate* predicate = [NSPredicate predicateWithFormat:#"%K == %#",#"name", searchStr]; //will filter according to "name" key in your dictionary.
[_mArrFilteredList addObjectsFromArray:[_mArrList filteredArrayUsingPredicate:predicate]]; //_mArrList is your main array which has all the dictionaries.
[yourTableView reloadData];
}
Now in your tableview datasource methods, populate table using filtered array(_mArrFilteredList).

For any search implementations we require two lists, one original and other filtered. We will usually use filtered list to fill up the table.
In your case, you can have dictionary as original list. For filtered list you can have a NSArray of values or keys according to requirements.
Filter the array list in search function, reload the table. In cellForRow get the object from the array keys or get key from array values- later object for that key.

Related

How to Display this kind of data into UITableview

I am having NSArray in which it is having following data, i am using AFHTTPRequestOperation which give me result and after that i am doing
[ListOfName addObject:[responseData valueForKey:name]]; and getting following result and that result i want to display in tableview but can understand how to do it becuause i an new to iphone
(
(
"Richard Conover",
"Richard Conover",
"Kaitlyn Matheson",
"Andrea Wannemaker",
"Andrea Wannemaker",
test,
james,
test,
gaurav,
sdfsdfs
)
)
if i do NSArray.count it will return only 1 so how to print it separately in tableview
What you want to do is set the TableView datasource to be the first object in that Array (which is another Array). Something like this:
NSArray *myTableViewDataSourceArray = [myOriginalArray objectAtIndex:0];
Then use myTableViewDataSourceArray for the datasource methods of the TableView.
As per your structure, you having array with objects which also array.
That means,[ListOfName addObject:[responseData valueForKey:name]]; will add array object. You try to load this array of data into your table view. So you can try this in your tableViewDataSource
- (NSInteger)numberOfSectionsInTableView:(UITableView *)aTableView
{
return [[ListOfName objectAtIndex:0] count];
}
It's a Nested Array (means Array inside Array).
Howmany Nested Arrays you have ?
If it's 1 then you can use :
NSArray *finalArray = [[NSArray alloc] initWithArray:[yourArray objectAtIndex:0]];
then you can use finalArray to populate the UITableView.

How do I add objects to an array that is inside an array?

I am trying to add an object to an array that is inside an array.
Here is my storyboard. Screen A is a simple tableView containing an array with object A, Screen B adds new objects to screen A. Each object A contains an array with detail (object B), these details are shown in screen C and you add details to object A in screen D.
So my model is as you can see above. I got Array A containing object A, each object contains Array B containing object B. Both my arrays are Mutable.
Object A = budget
Object B = item
I can not figure out how to add object B to array B.
- (void)addItemViewController:(AddItemViewController *)controller didFinishAddingItem:(Item *)item
int newRowIndex = [self.budgets.items count];
[self.dataModel.budgetsList addObjectFromArray:item];
NSLog(#"Item with name %# added", item.name);
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:newRowIndex inSection:0];
NSArray *indexPaths = [NSArray arrayWithObject:indexPath];
[self.tableView insertRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationAutomatic];
[self dismissViewControllerAnimated:YES completion:nil];
This is what I am doing so fare. My problem here is that I am adding item (object B) into budget array (array A). :/
Thanks in advance.
When you are doing this:
NSArray *indexPaths = [NSArray arrayWithObject:indexPath];
You are mixing Presentation with data. What you need here is get the object element (the array in your case) that this indexpath corresponds to. As per the Table view design pattern, every table view reads its cells from an underlying collection of data objects. Have you defined this object (most preferably in separate objective-c .m and .h files)?
As for adding array into another array, NSArray just expects NSObject as element, so it is pretty straight-forward to add one into another.
NSArray *arrayB = [[NSArray alloc] init]; //any other initialization is good as well
NSArray *arrayA= [NSArray arrayWithObject:arrayB];
The above code is valid for any pair of NSArrays in your code.
If you want to add an object to Array B, then Use:
[[[array A objectAtIndex:indexPath] arrayB] addObject:yourObject];
Or you can use (this is an expansion of above code):
ObjectA *temp = [array A objectAtIndex:indexPath];
NSMutableArray *tempArray = [temp arrayB];
[tempArray addObject:yourObject];
Cast your object B to Item then do
[self.dataModel.budgetList replaceObjectAtIndex:11 withObject:(Item)item];
This code assume that you want to replace the existing object inside A and the the index is 11. If you want to add you just use insertObjectAtIndex: withObject:

Search for strings in large array takes a long time

I'm implementing a search field that filters a UITableView according to the text the user enters.
The TableView is built from an array that holds NSStrings (the data to display and search) and may contain 6000+ items.
When the user starts the search, I'm implementing the -(void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText method.
My code works, however, when the data array is large, it is very slow and creating a really bad user experience (my iPhone 4s get stuck for a good few seconds).
The way I'm implementing the search (in the method mentioned above) is this:
NSMutableArray *discardedItems = [[NSMutableArray alloc] init]; // Items to be removed
searchResultsArray = [[NSMutableArray alloc] initWithArray:containerArray]; // The array that holds all the data
// Search for matching results
for (int i=0; i<[searchResultsArray count]; i++) {
NSString *data = [[containerArray objectAtIndex:i] lowercaseString];
NSRange r = [data rangeOfString:searchText];
if (r.location == NSNotFound) {
// Mark the items to be removed
[discardedItems addObject:[searchResultsArray objectAtIndex:i]];
}
}
// update the display array
[searchResultsArray removeObjectsInArray:discardedItems];
[myTableView reloadData];
I did not think that looping over an array with a few thousand items would cause any issue...
Any suggestion will be appreciated!
UPDATE
I've just realized that what takes most of the time is this:
[searchResultsArray removeObjectsInArray:discardedItems];
Try fast enumeration way, my snippet:
- (void)searchBar:(UISearchBar*)searchBar textDidChange:(NSString*)text
{
if(text.length == 0)
{
self.isFiltered = NO;
}
else
{
self.isFiltered = YES;
self.searchArray = [NSMutableArray arrayWithCapacity:self.places.count];
for (PTGPlace* place in self.places)
{
NSRange nameRange = [place.name rangeOfString:text options:NSCaseInsensitiveSearch];
if(nameRange.location != NSNotFound)
{
[self.searchArray addObject:place];
}
}
}
[self.tableView reloadData];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if(self.isFiltered)
return self.searchArray.count;
else
return self.places.count;
}
In cellForRowAtIndexPath:
PTGPlace *place = nil;
if(self.isFiltered)
place = [self.searchArray objectAtIndex:indexPath.row];
else
place = [self.places objectAtIndex:indexPath.row];
// Configure the cell...
cell.textLabel.text = place.name;
cell.detailTextLabel.text = [place subtitle];
Try this:
for the first three positions, create 26 index sets, each representing the array index of items with that letter (just lower case). That is, say a entry at idx=100 starts with "formula". The index set representing "f" in the first position will contain the index "100". The index set for the second character 'o' will contain the index 100, and the index set for the third character 'r' will contain 100.
When the user types the character 'f', you immediately have the index set of all array items starting with 'f' (and can create a subset of your major array quickly). When an 'o' is typed next, you can find the intersection of indexes in the first match with the second match. Ditto for the third. Now make a subarray of the major array that had the first three indexes match - you can just use the index sets for this.
Using this drastically reduced array, you can now do brute force matching as you were doing originally.

Core Data sectionNameKeyPath Custom String

When sorting a table of objects from Core Data, I'd like to set a custom string for the section heading that includes an attribute. For example, I'd like the section name to display "4 Stars", instead of just 4. I've fiddle with it, but It seems to get grumpy if I try to set the string for the sectionNameKeyPath to anything other than an Entity Attribute and only an entity attribute. Here's what works for attribute only, and one of a few attempts to customize the string which breaks is commented out.
NSSortDescriptor *ratingDescriptor = [[NSSortDescriptor alloc] initWithKey:#"starRating" ascending:NO];
sortDescriptors = [NSArray arrayWithObjects:ratingDescriptor, nameDescriptor, nil];
[ratingDescriptor release], ratingDescriptor = nil;
// NSString *starSectionHeading = [NSString stringWithFormat:#"%d Stars", #"starRating"];
// sectionKeyPath = starSectionHeading;
sectionKeyPath = #"starRating";
Set your sectionNameKeyPath to the "starRating" but then modify the output in the table view. The FRC will sort things and tidy things up in sections you just have to change what you would normally display as the header string.
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
// Display the stars as section headings.
int stars = [[[[fetchedResultsController sections] objectAtIndex:section] valueForKey:#"name"] intValue];
if(stars == 1)
{
return #"1 Star"
}
else
{
return [NSString stringWithFormat:#"%u Stars", stars];
}
}
I do this in some table views where the output format is handled in a generic fashion (I delegate the header titles to a another controller class given the first sort descriptor path and the value of the title). So you are not limited to hard coding the table view delegate methods like the above code.
You also get a chance to localize the string here as well, I have to deal with 15 localizations in my app and you have to think about things a bit differently when localizing.
The sectionNameKeyPath is supposed to be a key path i.e. the name of single attribute or the name of a relationship that terminates in a single attribute. You are trying to create a composite of two attributes and the FRC does not support that automatically.
To get something more fancy you will have to subclass NSFetchedResultsController. From the docs.
You create a subclass of this class if
you want to customize the creation of
sections and index titles. You
override
sectionIndexTitleForSectionName: if
you want the section index title to be
something other than the capitalized
first letter of the section name. You
override sectionIndexTitles if you
want the index titles to be something
other than the array created by
calling
sectionIndexTitleForSectionName: on
all the known sections.
Look at this answer with creating transient attribute:
NSFetchedResultsController with sections created by first letter of a string
just change some names and in your version of committeeNameInitial replace:
[[self committeeName] substringToIndex:1];
with
[NSString stringWithFormat:#"%# Stars", [self starRating]];
(NSString *)tableView:(UITableView *)aTableView titleForHeaderInSection:(NSInteger)section
{
if ([self.fetchedResultsController sections].count > 0) {
id <NSFetchedResultsSectionInfo> sectionInfo =
[[self.fetchedResultsController sections] objectAtIndex:section];
return [sectionInfo name];
}
return nil;
}

iPhone - Sort UITableView by Array Index

I have an UITableView set up with a NSArray with 10 indexes. Right now, the first cell on the uitableview is the first index, the second cell is the second index, and so on so forth. Is there any way to make it so that the first cell displays the latest index? Maybe through some code in the uitableview delegate because I am adding data to the NSArray. What that means is that there aren't 10 indexes right off the bat.
If anyone as an answer, help is much appreciated.
Each time that you get a new item of data, you add it to the start of your array, not to the end. Then just call [self.tableView reloadData] and it should just work.
You can use insertObject:atIndex: to add to the start of the array:
[myArray insertObject:newData atIndex:0];
(see here for docs)
Somewhere in your code you're probably doing something like this (where items is your NSArray object):
cell.textLabel.text = [items objectAtIndex:indexPath.row];
Instead, do:
cell.textLabel.text = [items objectAtIndex:([items count] - 1) - indexPath.row];