Create index for UITableView from an NSArray

2019-03-09 13:27发布

问题:

I've read that the best way of creating an index (the a-z at the side of a uitableview) is to set up an array of nsdictionaries, where each dictionary corresponds to a section, and a rowValue key contains an array of the rows.

NSDictionary
    headerTitle => ‘A’
    rowValues => {”Aardvark”, “Ape”, “Aquaman”}
NSDictionary
    headerTitle => ‘B’
    rowValues => {”Bat”, “Boot”, “Bubbles”} etc

But how can this be created from an array of all the row titles - {”Aardvark”, “Ape”, “Aquaman”, ”Bat”, “Boot”, “Bubbles”, "Cat", "Cabbage" etc} ...?

回答1:

#pragma mark -
#pragma mark View lifecycle
- (void)viewDidLoad
{
    [super viewDidLoad];
    NSMutableArray *temp = [[NSMutableArray alloc] init];
    NSMutableArray *temp2 = [[NSMutableArray alloc] init];
    for(int i = 0; i < tableListArray.count; i++)
    {
        NSString *string = [tableListArray objectAtIndex:i];
        NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
        [dict setObject:string forKey:@"Name"];
        [dict setObject:[NSNumber numberWithInt:i] forKey:@"ID"];
        NSString *firstString = [string substringToIndex:1];
        if([temp2 containsObject:firstString] == NO || temp2.count == 0)
        {
            if(temp2.count != 0)
            {
                [temp addObject:temp2];
                [temp2 release];
                temp2 = [[NSMutableArray alloc] init];
            }
            [temp2 addObject:firstString];
        }
        [temp2 addObject:dict];
        [dict release];
    }
    [temp addObject:temp2];
    detailListArray = [[NSArray alloc] initWithArray:temp];
    [temp release];
    [temp2 release];
}

#pragma mark -
#pragma mark Table view data source
- (NSInteger)tableView:(UITableView *)tableView 
sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index
{
    int i = 0;
    for(NSArray *array in detailListArray)
    {
        NSString *string = [array objectAtIndex:0];
        if([string compare:title] == NSOrderedSame)
            break;
        i++;
    }
    return i;
}

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return detailListArray.count;
}

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
    NSArray *array = [detailListArray objectAtIndex:section];
    return [array objectAtIndex:0];
}

- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView 
{
    NSMutableArray *titleArray = [NSMutableArray array];
    [titleArray addObject:@"A"];
    [titleArray addObject:@"B"];
    [titleArray addObject:@"C"];
    [titleArray addObject:@"D"];
    [titleArray addObject:@"E"];
    [titleArray addObject:@"F"];
    [titleArray addObject:@"G"];
    [titleArray addObject:@"H"];
    [titleArray addObject:@"I"];
    [titleArray addObject:@"J"];
    [titleArray addObject:@"K"];
    [titleArray addObject:@"L"];
    [titleArray addObject:@"M"];
    [titleArray addObject:@"N"];
    [titleArray addObject:@"O"];
    [titleArray addObject:@"P"];
    [titleArray addObject:@"Q"];
    [titleArray addObject:@"R"];
    [titleArray addObject:@"S"];
    [titleArray addObject:@"T"];
    [titleArray addObject:@"U"];
    [titleArray addObject:@"V"];
    [titleArray addObject:@"W"];
    [titleArray addObject:@"X"];
    [titleArray addObject:@"Y"];
    [titleArray addObject:@"Z"];
    return titleArray;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    NSArray *array = [detailListArray objectAtIndex:section];
    return (array.count - 1);
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
                                                    reuseIdentifier:@"CELL"] autorelease];
    NSArray *array = [detailListArray objectAtIndex:indexPath.section];
    NSDictionary *dict = [array objectAtIndex:indexPath.row + 1];
    cell.textLabel.text = [dict objectForKey:@"Name"];
    return cell;
}

#pragma mark -
#pragma mark Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSArray *array = [detailListArray objectAtIndex:indexPath.section];
    NSDictionary *dict = [array objectAtIndex:indexPath.row + 1];
    int entryID = [[dict objectForKey:@"ID"] intValue];
     // Do what ever you want to do with the selected row here....
}

This is the code that I have used in one of the recent projects.



回答2:

I recently had a similar objective and this is how I solved it. The advantage of this over Robin's solution is that it creates the index title array dynamically based on the content of your array and it won't include indices for empty sections (plus it's a little cleaner).

I created a category of NSMutableDictionary that takes an array of data as a parameter and returns an NSMutableDictionary (we'll call it indexDictionary and it should be an instance variable):

// if your data is static, you can call this in `viewDidLoad`
indexDictionary = [NSMutableDictionary createDictionaryForSectionIndex:arrayOfStrings];

The category method:

@implementation NSMutableDictionary (DictionaryForSectionIndex)

+(NSMutableDictionary *)createDictionaryForSectionIndex:(NSArray *)array
{
    NSMutableDictionary *dict = [NSMutableDictionary dictionary];
    for (char firstChar = 'a'; firstChar <= 'z'; firstChar++)
    {
        //NSPredicates are fast
        NSString *firstCharacter = [NSString stringWithFormat:@"%c", firstChar];
        NSArray *content = [array filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"SELF beginswith[cd] %@", firstCharacter]];
        NSMutableArray *mutableContent = [NSMutableArray arrayWithArray:content];

        if ([mutableContent count] > 0)
        {
            NSString *key = [firstCharacter uppercaseString];
            [dict setObject:mutableContent forKey:key];
            NSLog(@"%@: %u", key, [mutableContent count]);
        }
    }

    return dict;
}

@end

/*

**Input:** 
{"Aardvark", "Cabbage", "Boot", "Eggs", "Ape", "Aquaman", "Elephant", "Cat", "Bat", "Bubbles"}

**Output:**
NSMutableDictionary
    key => 'A'
    object => {"Aardvark", "Ape", "Aquaman"}

    key => 'B'
    object => {"Bat", "Boot", "Bubbles"}

    key => 'C'
    object => {"Cat", "Cabbage"}

    key => 'E'
    object => {"Elephant", "Eggs"}

*/

Then I create an NSArray instance variable to sort and store all the keys from indexDictionary:

// this line should follow the creation of `indexDictionary`

sortedKeys = [[indexDictionary allKeys] sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)];

// Output, in this case, is {'A', 'B', 'C', 'E'}

You now have everything you need to set up the index for your table. Implement the following methods (if something isn't self explanatory, just let me know):

//this code assumes `sortedKeys` is not empty

#pragma mark - UITableViewDataSource

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return ([sortedKeys count]);
}


- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    NSString *key = [sortedKeys objectAtIndex:section];
    return [[indexDictionary valueForKey:key] count];
}


-(NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
    return ([sortedKeys objectAtIndex:section]);
}


-(NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView
{
    return sortedKeys;
}


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


- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    // preceding code...

    NSString *key = [sortedKeys objectAtIndex:indexPath.section];
    NSArray *array = [indexDictionary objectForKey:key];
    NSString *yourString = [array objectAtIndex:indexPath.row];

    cell.textLabel.text = yourString;

    // following code...
}

The result is a table with an index that skips letters that have no associated data.



回答3:

I was working on one project where I need dynamic solutions for same problem so I figure out this solutions which returns dictionary with only available sections. Hope it help someone.

-(NSMutableDictionary *)getSortedDataWithArray:(NSArray *)dataArray{
  NSMutableDictionary *allDataDictionary = [[NSMutableDictionary alloc]init];

  for(int i = 0 ; i< dataArray.count ;i++){

    NSString *currentStr = [[[dataArray objectAtIndex:i] substringToIndex:1] uppercaseString];
    NSArray *allKeysArray = allDataDictionary.allKeys;

    if([allKeysArray containsObject:currentStr]){
        NSMutableArray *currentArray = [[NSMutableArray alloc] initWithArray:[allDataDictionary valueForKey:currentStr]];
        [currentArray addObject:[dataArray objectAtIndex:i]];
        NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF beginswith[c] %@" ,currentStr];
        NSArray *finalArray = [currentArray filteredArrayUsingPredicate:predicate];
        [allDataDictionary setValue:finalArray forKey:currentStr];
    }
    else{
        NSMutableArray *finalArray = [[NSMutableArray alloc] initWithObjects:[dataArray objectAtIndex:i], nil];

        [allDataDictionary setValue:finalArray forKey:currentStr];
    }
  }
 return allDataDictionary;
}