Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Create index for UITableView from an NSArray

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} ...?

like image 289
cannyboy Avatar asked Aug 23 '11 09:08

cannyboy


People also ask

What is indexPath Uitableview?

IndexPath contains information about which row in which section the function is asking about. Base on this numbers you are configuring the cell to display the data for given row.

Is Nsarray ordered?

The answer is yes, the order of the elements of an array will be maintained - because an array is an ordered collection of items, just like a string is an ordered sequence of characters...

What does indexPath row return?

So to start with the indexPath. row will be 0. Then it will be 1, then 2, then 3 and so on. You do this so that you can get the correct string from the array each time.

What is indexPath in Tableview Swift?

indexPath(for:)Returns an index path that represents the row and section of a specified table-view cell.


1 Answers

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.

like image 63
lobianco Avatar answered Oct 25 '22 08:10

lobianco