Hello again. This time I will be creating a native contacts like screen. If you are not familiar with it, here is how it looks like:
This view is very important for many business applications. There can be several ways of implementing this view, depending upon your specific need you can write one. I will discuss the simplest one in which I will be creating a dictionary to access names. I will be adding code to same DatabaseTest project which I created earlier.
For basics, refer my previous posts
So lets start and open the project DatabaseTest. We need not change anything in AppDelegate. Open RootViewController.h and add the following code:
@interface RootViewController : UITableViewController {
//NSMutableArray *tableData;
NSMutableArray *arrayOfCharacters;
NSMutableDictionary *objectsForCharacters;
}
@end
You will notice that I have commented tableData and declared an additional array and a dictionary. We wont be using tableData anymore instead data will be now stored in dictionary objectsForCharacters. You will understand how in some time now.
Open RootViewController.m and in -(void)initializeTableData comment all the code written and add this code:
arrayOfCharacters = [[NSMutableArray alloc]init];
objectsForCharacters = [[NSMutableDictionary alloc]init];
sqlite3 *db = [DatabaseTestAppDelegate getNewDBConnection];
for(char c=‘A’;c<=‘Z’;c++)
{
NSMutableString *query;
query = [NSMutableString stringWithFormat:@"select name from user where name LIKE '%c",c];
[query appendString:@"%'"];
char *sql = [query cString];
sqlite3_stmt *statement = nil;
if(sqlite3_prepare_v2(db,sql, -1, &statement, NULL)!= SQLITE_OK)
NSAssert1(0,@”error preparing statement”,sqlite3_errmsg(db));
else
{
NSMutableArray *arrayOfNames = [[NSMutableArray alloc]init];
while(sqlite3_step(statement)==SQLITE_ROW)
[arrayOfNames addObject:[NSStringtringWithFormat:@"%s",(char*)sqlite3_column_text(statement, 0)]];
if([arrayOfNames count] >0)
{
[arrayOfCharacters addObject:[NSString stringWithFormat:@"%c",c]];
[objectsForCharacters setObject:arrayOfNames forKey:[NSString stringWithFormat:@"%c",c]];
}
[arrayOfNames release];
}
sqlite3_finalize(statement);
}
I will explain what we are doing here. For each alphabetical character we are querying database to get all the names starting with that character. If we get 1 more or more names for a character, we add that character in an array arrayOfCharacters. Names are stored in a separate array arrayOfNames. This is a temporary array which will added to dictionary objectsForCharacters. At the end we have arrayOfCharacters holding all those characters which have at least one name starting with them respctively and we have objectsForCharacters holding an array of names for each character in arrayOfCharacters.
Now we need to fix the view. For this we will now be using 3 new tableView methods:
- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView
- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
The first method asks for an array of strings which it will use as index titles. You can find indexes A-Z vertically arranged on the rightmost side of tableView. Here you can decide either to give all the characters or just the ones in arrayOfCharacters.
In the second method you specify what should be the section number for the given index title.
In third method you provide the title for each section header. You can find in the first figure, a gray bar with a character appearing in it just above each section.
So lets implement these methods, add the following code:
- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView {
NSMutableArray *toBeReturned = [[NSMutableArray alloc]init];
for(char c = ‘A’;c<=‘Z’;c++)
[toBeReturned addObject:[NSString stringWithFormat:@"%c",c]];
return toBeReturned;
}
- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index {
NSInteger count = 0;
for(NSString *character in arrayOfCharacters)
{
if([character isEqualToString:title])
return count;
count ++;
}
return 0;// in case of some eror donot crash d application
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
if([arrayOfCharacters count]==0)
return @”";
return [arrayOfCharacters objectAtIndex:section];
}
I think I have already explained the code.
Before we build and run the project, we need to change the other tableView methods we created last time. Make sure these three methods look like these:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return [arrayOfCharacters count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [[objectsForCharacters objectForKey:[arrayOfCharacters objectAtIndex:section]] count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *MyIdentifier = @”MyIdentifier”;
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:MyIdentifier] autorelease];
}
// Set up the cell
cell.text = [[objectsForCharacters objectForKey:[arrayOfCharacters objectAtIndex:indexPath.section]] objectAtIndex:indexPath.row];
return cell;
}
I will try explaining a few thing here. [[objectsForCharacters objectForKey:[arrayOfCharacters objectAtIndex:section]] count]
Here we have first derived the character for the section. Then we used that character to obtain its corresponding array of names. And the count of names in the array will be numberOfRows in that section.
Rest should be clear.
Go ahead and run the project and you should see the screen similar to figure 1.
Also try adding more data into database and see how it looks.
Please write back if anything is not clear and I will be more than happy to help.
[...] Native Address Book Like Screen [...]
Hi, thanks for this tutorial, it helped me a lot to create the index section for the table. Just 2 things:
- cString is deprecated, maybe you can change it to [query UTF8String]?
- query needs not to be NSMutableString, you can use %% if you want to initialize a string with the % character.
True Jade. [query UTF8String] is good mostly when you are syncing data from/to a server. Thanks for the remark.
Hello, I can’t understand how to add your blog in my rss reader
————————
ads: http://xabul.ru/
We are kinda updating the blog layout, will add a widget soon
thanks
Thanks for this great tutorial. Very helpful.
Hey! Is there anyway to do this without sqlite database. I have parsed my data from an XML into my NSMutableArray. But then i don’t know how to get them ut of there?
help.. please :]
My mutable array look like this:
NSMutableArray *myArray = [NSMutableArray arrayWithObjects:@"A test", @"B test", @"C test", @"D test", nil];
i guess it’s just make everything the same way except in the initializeTableData method. But how???
:D
HI,
When I run this app I got this error :
” *** Terminating app due to uncaught exception ‘NSInternalInconsistencyException’, reason: ‘error preparing statement’ ”
can anyone help ?