How to implement UITableView search functionality - iphone

My iPhone application has a UITable View implemented with search functionality in it. The values in the table are grouped into sections from A-Z. Whenever a user tap on particular cell in the table it loads a detail view controller which gives all the values of that particular user. Now my problem is whenever I search some contact and tap on a particular user to check his detail view it always returns a contact starting with letter A. So, my doubt is how to implement this search functionality. Is there a way to get the name of the contact I tapped..Please check this screenshot.. For example if I search some contact starting with letter 'B' and tap on that contact it loads the detail view of a contact starting with letter 'A'. I get all the values from the database. Can you please help me out...
This is the code:
The code I wrote here is in a method:
I am getting all the contacts from database and assigning to an array contacts. Then I am grouping all the contacts according to the alphabets and grouping everything into a dictionary with keys as A-Z and values as name of contacts starting with these letters. Now when I search for a particular contact his name may start with either A ,B or Z..so in the search bar when I search for a particular contact for example a contact starting with letter Z, in this case it gives the details of a person with A. I want this to change so that whenever I tap on a particular contact it should load its details. I am unable to figure out how to do it..
contacts = [[db getContacts:#"Contacts"] componentsSeparatedByString:#","];
[db cleanup];
NSMutableArray *tempArray = [[NSMutableArray alloc] init];
NSString *charString;
for (int i=65; i<91; i++) {
charString = [NSString stringWithFormat:#"%c",(char *)i];
[tempArray addObject:charString];
}
[charString release];
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
for (int i=0; i<[tempArray count]; i++) {
NSMutableArray *contactsByIndex = [[[NSMutableArray alloc] init]autorelease];
NSString *tempChar = [tempArray objectAtIndex:i];
for (int j=0; j<[contacts count]-1; j++)
{
NSString *test = [contacts objectAtIndex:j];
NSString *tempString = [test substringToIndex:1];
if ([tempString isEqualToString:tempChar]) {
[contactsByIndex addObject:[contacts objectAtIndex:j]];
}
}
[dict setObject:contactsByIndex forKey:[tempArray objectAtIndex:i]];
}
self.contactNames = dict;
NSArray *array = [[contactNames allKeys] sortedArrayUsingSelector:#selector(caseInsensitiveCompare:)];
self.contactKeys = array;
[dict release];
[tempArray release];
//---display the searchbar---
self.tableView.tableHeaderView = searchBar;
searchBar.autocorrectionType = UITextAutocorrectionTypeYes;
listOfContacts = [[NSMutableArray alloc] init];
for (NSString *key in array)
{
NSArray *contactsArray = [contactNames objectForKey:key];
for (NSString *name in contactsArray) {
[listOfContacts addObject:name];
}
}
- (void) searchContactsTableView {
//---clears the search result---
[searchResult removeAllObjects];
for (NSString *str in listOfContacts) {
NSRange titleResultsRange = [str rangeOfString:searchBar.text options:NSCaseInsensitiveSearch];
if (titleResultsRange.length > 0)
[searchResult addObject:str];
}
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// Navigation logic may go here. Create and push another view controller.
NSString *selectedRow=nil;
if (isSearchOn) {
selectedRow=[searchResult objectAtIndex:indexPath.row];
}
DetailViewController *detailViewController;
int section_index=[indexPath indexAtPosition:[indexPath length]-2];
int sugarid_Index = [indexPath indexAtPosition: [indexPath length]-1];
NSString* sectionName=[contactKeys objectAtIndex:section_index];
NSLog(#"%#",sectionName);
//This is a method which gets the details of a particular contact based on the section and the row selected..Contacts is the table name
NSString *getContact=[db getId:#"Contacts" bySection:sectionName andIndex:sugarid_Index];
id=[db getContact:#"Contacts" id:getContact];
// Pass the selected object to the new view controller.
detailViewController = [[DetailViewController alloc] initWithNibName:#"DetailViewController" bundle:nil];
detailViewController.eachContact=contactForSugarId;
[self.navigationController pushViewController:detailViewController animated:YES];
}
When I search for a contact it should search for the name in database and should return its details. Is there a way to do it..please let me know or is there a way to get the name of the cell i.e. the contact name so that I can use that in one of my database methods to retrieve the details of the contact I selected.

Off hand it sounds like you're looking in the wrong array of contacts after the search. You need to have two arrays. One with all the contacts, and one with the filtered contacts. When you search, put all the results in order in the filtered list, and pull the details from that one.
Does this make sense?
If not, try posting a bit of code, and explaining your structure.

Related

Performance issue creating Section Index Titles for UITableView

I'm displaying an array of contacts ( [[ContactStore sharedStore]allContacts] ) in a tableview and have divided the list into alphabetic sections. I have used the following code to return an array of the first letters of the contacts, and a dictionary of the number of entries per letter.
//create an array of the first letters of the names in the sharedStore
nameIndex = [[NSMutableArray alloc] init];
//create a dictionary to save the number of names for each first letter
nameIndexCount = [[NSMutableDictionary alloc]init];
for (int i=0; i<[[[ContactStore sharedStore]allContacts]count]; i++){
//Get the first letter and the name of each person
Contact *p = [[[ContactStore sharedStore]allContacts]objectAtIndex:i];
NSString *lastName = [p lastName];
NSString *alphabet = [lastName substringToIndex:1];
//If that letter is absent from the dictionary then add it and set its value as 1
if ([nameIndexCount objectForKey:alphabet] == nil) {
[nameIndex addObject:alphabet];
[nameIndexCount setValue:#"1" forKey:alphabet];
//If its already present add one to its value
} else {
NSString *newValue = [NSString stringWithFormat:#"%d", ([[nameIndexCount valueForKey:alphabet] intValue] + 1)];
[nameIndexCount setValue:newValue forKey:alphabet];
}
}
This works, however it is very slow when the array is large, I'm sure there's a better way to do this but I'm quite new to this so am not sure how. Are there any suggestions for a better way to do this?
Although Bio Cho has a good point, you might see an increase in performance by calling
[[ContactStore sharedStore]allContacts]
only once. For example:
nameIndex = [[NSMutableArray alloc] init];
nameIndexCount = [[NSMutableDictionary alloc] init];
/*
Create our own copy of the contacts only once and reuse it
*/
NSArray* allContacts = [[ContactStore sharedStore] allContacts];
for (int i=0; i<[allContacts count]; i++){
//Get the first letter and the name of each person
Contact *p = allContacts[i];
NSString *lastName = [p lastName];
NSString *alphabet = [lastName substringToIndex:1];
//If that letter is absent from the dictionary then add it and set its value as 1
if ([nameIndexCount objectForKey:alphabet] == nil) {
[nameIndex addObject:alphabet];
[nameIndexCount setValue:#"1" forKey:alphabet];
//If its already present add one to its value
} else {
NSString *newValue = [NSString stringWithFormat:#"%d", ([[nameIndexCount
valueForKey:alphabet] intValue] + 1)];
[nameIndexCount setValue:newValue forKey:alphabet];
}
}
Though I can't say for sure, I'd guess that repeatedly accessing your shared store is what's killing you. Maybe only accessing it once will give you what you need.
Consider storing your contacts in Core Data and using an NSFetchedResultsController.
The NSFetchedResultsController will only load a subset of the rows which are visible on the table view, thus preventing your user from having to wait for all the contacts to be sorted.
NSFetchedResultsController will also sort your contacts by an attribute (ie. first or last name), and you can set your section titles to be the first letter of the field you're sorting by.
Take a look at this question and this tutorial.

retrieved data from sqlite database and displaying it on grouped table view

hii every one
i am brand new to obj c, i have did a sample project where i have 2 screens on the first screen i have six text fields & 2 buttons named save and ViewData ,on click of save data which is entere d in the text field will be get saved in the sqliteData Base ,& on click of the button ViewData it will navigate to a new screen which has a grouped table view, here i am trying to display the data which is stored in the sqlite ,in the grouped table view i have 6 sections i am using following code to display the data in grouped table view,problem is grouped table view is displaing only the last data which is entered ih the text field,,but i need to display all the data which enterd should be shown under that section
appDelegate = (iICS_testAppDelegate *)[[UIApplication sharedApplication] delegate];
for(int intVar=0;intVar < [appDelegate.arrObjects count];intVar++)
{
insertUpdateDelete *InsertRecord = [appDelegate.arrObjects objectAtIndex:intVar];
NSLog(#"InsertRecord:%#",InsertRecord);
NSMutableArray *arrTemp1 = [[NSMutableArray alloc]initWithObjects:InsertRecord.strsixth,nil];
NSMutableArray *arrTemp2 = [[NSMutableArray alloc]initWithObjects:InsertRecord.strfifth,nil];
NSMutableArray *arrTemp3 = [[NSMutableArray alloc]initWithObjects:InsertRecord.strFourth,nil];
NSMutableArray *arrTemp4 = [[NSMutableArray alloc]initWithObjects:InsertRecord.strLogin,nil];
NSMutableArray *arrTemp5 = [[NSMutableArray alloc]initWithObjects:InsertRecord.strMiddleName,nil];
NSMutableArray *arrTemp6 = [[NSMutableArray alloc]initWithObjects:InsertRecord.strFirstName,nil];
NSMutableDictionary *temp =[[NSMutableDictionary alloc]initWithObjectsAndKeys:arrTemp1,#"Item Name",arrTemp2,#"Manufacturer",arrTemp3,#"Weight of Item",arrTemp4,#"Num of Item",arrTemp5,#"Price of Item",arrTemp6,#"MFG Date",nil];
self.tableContents =temp;
[temp release];
NSLog(#"table %#",self.tableContents);
NSLog(#"table with Keys %#",[self.tableContents allKeys]);
self.sortedKeys =[[self.tableContents allKeys] sortedArrayUsingSelector:#selector(compare:)];
NSLog(#"sorted %#",self.sortedKeys);
[arrTemp1 release];
[arrTemp2 release];
[arrTemp3 release];
[arrTemp4 release];
[arrTemp5 release];
[arrTemp6 release];
}
here im assigning the text for the row
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
//insertUpdateDelete *InsertRecord = [appDelegate.arrObjects objectAtIndex:indexPath.row];
static NSString *SimpleTableIdentifier = #"SimpleTableIdentifier";
NSArray *listData =[self.tableContents objectForKey:[self.sortedKeys objectAtIndex:[indexPath section]]];
UITableViewCell * cell = [tableView
dequeueReusableCellWithIdentifier:SimpleTableIdentifier];
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:SimpleTableIdentifier] autorelease];
}
NSUInteger row = [indexPath row];
//cell.textLabel.text = [appDelegate.arrObjects objectAtIndex:row];
cell.textLabel.text = [listData objectAtIndex:row];
return cell;
}
thanks in advance!
This is my snapshopt of grouped table view. I need the data which is entered through the text field shoul be viewed under particular section
Solution for only the last data is displaying,
Instead of NSArray use NSMutableArray.
Solution for wrong field values,
Your problem may be in insertion itself,
NSArray *arrTemp1 = [[NSArray alloc]initWithObjects:InsertRecord.strsixth,nil];
NSArray *arrTemp2 = [[NSArray alloc]initWithObjects:InsertRecord.strfifth,nil];
You are inserting price value into date field, i think so. Please check that.
Change your code as,
NSMutableArray *arrTemp1 = [[NSMutableArray alloc]init];
NSMutableArray *arrTemp2 = [[NSMutableArray alloc]init];
NSMutableArray *arrTemp3 = [[NSMutableArray alloc]init];
NSMutableArray *arrTemp4 = [[NSMutableArray alloc]init];
NSMutableArray *arrTemp5 = [[NSMutableArray alloc]init];
NSMutableArray *arrTemp6 = [[NSMutableArray alloc]init];
for(int intVar=0;intVar < [appDelegate.arrObjects count];intVar++)
{
insertUpdateDelete *InsertRecord = [appDelegate.arrObjects objectAtIndex:intVar];
NSLog(#"InsertRecord:%#",InsertRecord);
[arrTemp1 addObject:InsertRecord.strsixth];
[arrTemp2 addObject:InsertRecord.strfifth];
[arrTemp3 addObject:InsertRecord.strFourth];
[arrTemp4 addObject:InsertRecord.strLogin];
[arrTemp5 addObject:InsertRecord.strMiddleName];
[arrTemp6 addObject:InsertRecord.strMiddleName];
}
NSDictionary *temp =[[NSDictionary alloc]initWithObjectsAndKeys:arrTemp1,#"Item Name",arrTemp2,#"Manufacturer",arrTemp3,#"Weight of Item",arrTemp4,#"Num of Item",arrTemp5,#"Price of Item",arrTemp6,#"MFG Date",nil];
self.tableContents =temp;
[temp release];
NSLog(#"table %#",self.tableContents);
NSLog(#"table with Keys %#",[self.tableContents allKeys]);
self.sortedKeys =[[self.tableContents allKeys] sortedArrayUsingSelector:#selector(compare:)];
NSLog(#"sorted %#",self.sortedKeys);
[arrTemp1 release];
[arrTemp2 release];
[arrTemp3 release];
[arrTemp4 release];
[arrTemp5 release];
[arrTemp6 release];

Help with Search bar scope buttons

I have a UI that displays data from a user table like FirstName, LastName, Email,etc. Now i want to create a search bar along with scope buttons that filters data depending on the scope button clicked. I have 2 scope buttons, FirstName and LastName. By default FirstName button is selected. Below is how I add my data to a mutablearray,
userData = [[NSMutableArray alloc] init];
for (NSDictionary *tmpDic in response) {
[userData addObject: [NSString stringWithFormat: #"%# %#",
[tmpDic valueForKey: #"FirstName"],[tmpDic valueForKey: #"LastName"]]];
}
My search code,
- (void) searchTableView {
NSString *searchText = theSearchBar.text;
NSMutableArray *searchArray = [[NSMutableArray alloc] init];
for (NSString *sTemp in userData)
{
NSRange titleResultsRange = [sTemp rangeOfString:searchText options:NSCaseInsensitiveSearch];
if (titleResultsRange.length > 0)
[copyuserData addObject:sTemp];
}
NSLog(#"Copied data is:%#", copyuserData);
[searchArray release];
searchArray = nil;
}
The above code works well for searching the userData array, but i am not sure how will i change the code so that depending on FirstName, LastName scope buttons it will display the result. how i will hook up the buttons to the search bar so that it only display result depending on what scope bar button is clicked. Thanks in advance..
You need to do two things:
1. Look at the value of the searchBar.selectedScopeButtonIndex - this will tell you if you need to search first names or last names.
2. Depending on the scope button, you want to search either the first part of each array item or the second part. There are lots of ways to do this. Probably the easiest is to keep 2 parallel arrays, firstNames and lastNames, which you populate from tmpDic. Then for the actual search, you could either loop through firstNames or lastNames, or do a for(int j=0;j<[firstNames count]; j++) and get [firstNames objectAtIndex:j] and compare that to your temp string. If any string matches, add it to your results array.

How can I implement search Objective-c?

I have a record like this.
FirstName - LastName - PhoneNo - Address - Designation
A - Mick - 789367789 - New york - Professor
B - Jossef - 534647458 - USA - Doctor
C - Sha - 342576765 - USA - Doctor
D - Gee - 535346457 - USA - Business Man
......Like this
I am displaying one FirstName in UITableView with UISearchBar. Search is working fine it filters data according the keyword your typing in the SearchBar but suppose you are typing M it list all the items of M and after then when i click on the first item of M, it displays the Details of A rather then M on the next View.
I think you can understand my problem.
How can i resolve this how to pass multiple values in the Next view corresponding to record.?
Thanks,
This function i took from the same code of TableView(apple sample code with searchbar ).
and i modified this according to my logic.
- (void) searchTableView
{
NSString *searchText = searchBar.text;
NSMutableArray *searchArray = [[NSMutableArray alloc] init];
NSInteger TotalNoOfRecords=[self.SearchtableDataSource count];
for (int i=0;i<TotalNoOfRecords;i++)
{ NSDictionary *dictionary = [self.SearchtableDataSource objectAtIndex:i];
NSArray *array = [dictionary objectForKey:#"Title"];
NSString *arrayID= [dictionary objectForKey:#"ID"];
NSLog(#"Testing - Id-%d",arrayID);
[searchArrayID addObject:arrayID];
[searchArray addObject:array];
}
for (NSString *sTemp in searchArray)
{
NSRange titleResultsRange = [sTemp rangeOfString:searchText options:NSCaseInsensitiveSearch];
if (titleResultsRange.length > 0)
{
[copyListOfItems addObject:sTemp];
}
}
[searchArray release];
searchArray = nil;
}
This is my didSelectRowAtIndexPath code i know this need modification but i don't know how?
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSDictionary *dictionary = [self.SearchtableDataSource objectAtIndex:indexPath.row];
FirstName= [dictionary objectForKey:#"FirstName"];
LastName=[dictionary objectForKey:#"LastName"];
DetailViewController *ivc = [[DetailViewController alloc] initWithNibName:#"DetailViewController" bundle:[NSBundle mainBundle]];
ivc.FirstName = FirstName;
ivc.LastName=LastName;
ivc.Title=[dictionary objectForKey:#"Details of Person"];
[self.navigationController pushViewController:ivc animated:YES];
[ivc release];
}
Please help me out...
Thanks
Check that you are retrieving the value from copyListOfItems in -didSelectRowAtIndexPath.
EDIT:
You should clearly understand the logic first. SearchedTableDatasource contains the tableViews complete set of data to be loaded in the tableview. copyListofItems contains filtered items. So in didSelectRowAtIndexPath you should code as follows,
if(searching==YES)
{
//retrieve the values from copyListofItems array
}
else
{
//retrieve the values from SearchedTableDatasource array
}
And also you are using a void function for searching, if so declare the copyListofItems array as global, or else make the function to return a NSMutableArray instance.
Change your init method in didSelectrowatIndexPath method and pass the dictionary object and set the DetailViewController attributes there, i hope that will work as it worked for me.

Adding # & search sign to TableIndex in UITableView

In iPhone native Phone book - there is a search character at the top & # character at the bottom.
I want to add both of that character in my table Index.
Currently I have implemented following code.
atoz=[[NSMutableArray alloc] init];
for(int i=0;i<26;i++){
[atoz addObject:[NSString stringWithFormat:#"%c",i+65]];
}
- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView{
return atoz;
}
How to have # character & search symbol in my UITableView?
The best way to tackle this is to make use of the tools the framework provides. In this case, you want to use UILocalizedIndexedCollation (developer link).
I also have a decorator for this class that is designed to insert the {{search}} icon for you and handle the offsets. It is a like-for-like drop-in replacement for UILocalizedIndexedCollation.
I've posted a more in-depth description of how to use this on my blog. The decorator is available here (Gist).
The basic idea is to group your collection into an array of arrays, with each array representing a section. You can use UILocalizedIndexedCollation (or my replacement) to do this. Here's a small NSArray category method I use to do this:
#implementation NSArray (Indexing)
- (NSArray *)indexUsingCollation:(UILocalizedIndexedCollation *)collation withSelector:(SEL)selector;
{
NSMutableArray *indexedCollection;
NSInteger index, sectionTitlesCount = [[collation sectionTitles] count];
indexedCollection = [[NSMutableArray alloc] initWithCapacity:sectionTitlesCount];
for (index = 0; index < sectionTitlesCount; index++) {
NSMutableArray *array = [[NSMutableArray alloc] init];
[indexedCollection addObject:array];
[array release];
}
// Segregate the data into the appropriate section
for (id object in self) {
NSInteger sectionNumber = [collation sectionForObject:object collationStringSelector:selector];
[[indexedCollection objectAtIndex:sectionNumber] addObject:object];
}
// Now that all the data's in place, each section array needs to be sorted.
for (index = 0; index < sectionTitlesCount; index++) {
NSMutableArray *arrayForSection = [indexedCollection objectAtIndex:index];
NSArray *sortedArray = [collation sortedArrayFromArray:arrayForSection collationStringSelector:selector];
[indexedCollection replaceObjectAtIndex:index withObject:sortedArray];
}
NSArray *immutableCollection = [indexedCollection copy];
[indexedCollection release];
return [immutableCollection autorelease];
}
#end
So, given an array of objects, for example books that I want to divide into sections based on their name (the Book class has a name method), I would do this:
NSArray *books = [self getBooks]; // etc...
UILocalizedIndexedCollation *collation = [UILocalizedIndexedCollation currentCollation];
NSArray *indexedBooks = [books indexUsingCollation:collation withSelector:#selector(name)];