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} ...?
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.
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...
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.
indexPath(for:)Returns an index path that represents the row and section of a specified table-view cell.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With