Thursday, August 2, 2012

iPhone Development: Creating Native Contacts like screen

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. 
You can find DatabaseTest here

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 allocinitWithFrame: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.

No comments:

Post a Comment