Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make a tableview divided into sections by letter like the contacts app

I'm trying to replicate the contacts table view in my app. So I have a list of contacts displayed in a table view however I would like the table view to be sectioned into all the letters of the alphabet and the names of the contacts to be placed in the section related to the lister letter of their first name. Like this

enter image description here

How do I get that view? So far this is all I have done.

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{

        return [displayNames count];

}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *simpleTableIdentifier = @"ContactsCell";

    /*ContactCell *cell = (ContactCell *)[tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];


    if (cell == nil) {
        NSArray *nib = [[NSBundle mainBundle] loadNibNamed:@"ContactCell" owner:self options:nil];
        cell = [nib objectAtIndex:0];
    }*/
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableIdentifier];
    }
     //   cell.name.text = [displayNames
       //                        objectAtIndex:indexPath.row];
    UILabel *namer = (UILabel *)[cell viewWithTag:101];
    namer.text=[displayNames
                    objectAtIndex:indexPath.row];
    /*
     Get the picture urls from the picture array and then 
     I loop through the array and initialize all the urls with 
     a NSUrl and place the loaded urls in anouther nsmutable array
     */
    urls = [[NSMutableArray alloc] init];

    for (id object in pictures) {
        //NSDictionary *names = res[@"image"];
        NSString *name = object;
        NSURL *url=[[NSURL alloc] initWithString:name];
        [urls addObject:url];
    }
   // cell.profile.image= [UIImage imageWithData:[NSData dataWithContentsOfURL: [urls objectAtIndex:indexPath.row]]];
    UIImageView *profiler = (UIImageView *)[cell viewWithTag:100];
    profiler.image= [UIImage imageWithData:[NSData dataWithContentsOfURL: [urls objectAtIndex:indexPath.row]]];


    return cell;
}
like image 808
Michael Nana Avatar asked Oct 21 '22 02:10

Michael Nana


1 Answers

Here is an easy solution using the 3rd party TLIndexPathTools data model class TLIndexPathDataModel. It is specifically designed for working with index paths and sections, so you can accomplish what you need with minimal complexity. And here is a full, working demo.

First define a class to represent a contact. This gives you a place to define firstName, lastName, displayName and sectionName:

@interface Contact : NSObject
@property (strong, nonatomic, readonly) NSString *firstName;
@property (strong, nonatomic, readonly) NSString *lastName;
@property (strong, nonatomic, readonly) NSString *displayName;
@property (strong, nonatomic, readonly) NSString *sectionName;
- (instancetype)initWithFirstName:(NSString *)firstName lastName:(NSString *)lastName;
@end

The sectionName property just returns the first character of the firstName. Then if your table view subclasses TLTableViewController, the implementation would look something like this:

@implementation ContactsTableViewController

- (void)viewDidLoad
{
    [super viewDidLoad];

    NSMutableArray *contacts = [NSMutableArray array];

    //get actual list of contacts here...
    [contacts addObject:[[Contact alloc] initWithFirstName:@"John" lastName:@"Doe"]];
    [contacts addObject:[[Contact alloc] initWithFirstName:@"Sally" lastName:@"Smith"]];
    [contacts addObject:[[Contact alloc] initWithFirstName:@"Bob" lastName:@"Marley"]];
    [contacts addObject:[[Contact alloc] initWithFirstName:@"Tim" lastName:@"Cook"]];
    [contacts addObject:[[Contact alloc] initWithFirstName:@"Jony" lastName:@"Ives"]];
    [contacts addObject:[[Contact alloc] initWithFirstName:@"Henry" lastName:@"Ford"]];

    //sort by section name
    [contacts sortUsingDescriptors:@[[NSSortDescriptor sortDescriptorWithKey:@"sectionName" ascending:YES]]];

    //set the data model
    self.indexPathController.dataModel = [[TLIndexPathDataModel alloc] initWithItems:contacts sectionNameKeyPath:@"sectionName" identifierKeyPath:nil];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [super tableView:tableView cellForRowAtIndexPath:indexPath];

    //get contact for index path from data model and configure cell
    Contact *contact = [self.indexPathController.dataModel itemAtIndexPath:indexPath];
    cell.textLabel.text = contact.displayName;

    return cell;
}

- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView
{
    return self.indexPathController.dataModel.sectionNames;
}

- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index
{
    return index;
}

@end

The key thing is that TLIndexPathDataModel automatically organizes your data into sections using the sectionNameKeyPath set to @"sectionName". Then in your view controller logic, you can easily access the contact for a given index path by calling:

Contact *contact = [self.indexPathController.dataModel itemAtIndexPath:indexPath];

update

You'd actually want to do a second-level sort on display name:

[contacts sortUsingDescriptors:@[[NSSortDescriptor sortDescriptorWithKey:@"sectionName" ascending:YES], [NSSortDescriptor sortDescriptorWithKey:@"displayName" ascending:YES]]];

update #2

There is a new block-based initializer for TLIndexPathDataModel that makes this a lot easier if you don't want to define a custom data object just to add a sectionNameKeyPath property. For example, one can use the new initializer to organize a list of strings as illustrated in the "Blocks" sample project:

- (void)viewDidLoad
{
    [super viewDidLoad];

    NSArray *items = [@[
           @"Fredricksburg",
           @"Jelly Bean",
           ...
           @"Metadata",
           @"Fundamental",
           @"Cellar Door"] sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)];

    //generate section names by taking the first letter of each item
    self.indexPathController.dataModel = [[TLIndexPathDataModel alloc] initWithItems:items
                                                                    sectionNameBlock:^NSString *(id item) {
        return [((NSString *)item) substringToIndex:1];
    } identifierBlock:nil];
}
like image 194
Timothy Moose Avatar answered Nov 01 '22 12:11

Timothy Moose