In the iPhone music app, selecting Artist, Songs, or Albums presents a tableView with a verticl list of single letters at the righthand side of the UI that enables rapid scr
If you're using a NSFetchedResultsController
, you can just do:
- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView {
return [frc sectionIndexTitles];
}
- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index {
return [frc sectionForSectionIndexTitle:title atIndex:index];
}
I came up with an alternative approach to handling a single letter alphabet list without using sections. It's similar to Zaph's answer but instead of getting any value from returning a new index (since we'll always have 1 section), we calculate the index for the location of the first item in the array that begins with a certain character, then scroll to it.
The downside is this requires searching the array every time (is this absolutely terrible?), however I didn't notice any lag or slow behavior in the iOS simulator or on my iPhone 4S.
- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView {
return[NSArray arrayWithObjects:@"A", @"B", @"C", @"D", @"E", @"F", @"G", @"H", @"I", @"J", @"K", @"L", @"M", @"N", @"O", @"P", @"Q", @"R", @"S", @"T", @"U", @"V", @"W", @"X", @"Y", @"Z", nil];
}
- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index {
NSInteger newRow = [self indexForFirstChar:title inArray:self.yourStringArray];
NSIndexPath *newIndexPath = [NSIndexPath indexPathForRow:newRow inSection:0];
[tableView scrollToRowAtIndexPath:newIndexPath atScrollPosition:UITableViewScrollPositionTop animated:NO];
return index;
}
// Return the index for the location of the first item in an array that begins with a certain character
- (NSInteger)indexForFirstChar:(NSString *)character inArray:(NSArray *)array
{
NSUInteger count = 0;
for (NSString *str in array) {
if ([str hasPrefix:character]) {
return count;
}
count++;
}
return 0;
}
adding property to store last selected index like
@property (assign, nonatomic) NSInteger previousSearchIndex;
and storing this property every time like:
- (NSInteger)indexForFirstChar:(NSString *)character inArray:(NSArray *)array
{
NSUInteger count = 0;
for (NSString *str in array) {
if ([str hasPrefix:character]) {
self.previousSearchIndex = count;
return count;
}
count++;
}
return self.previousSearchIndex;
}
and updating scrollToRow
code like:
[tableView scrollToRowAtIndexPath:newIndexPath atScrollPosition:UITableViewScrollPositionTop animated:YES];
Do this method even better and with nice animation.
If you're using MonoTouch, override the SectionIndexTitles(UITableView) method in the UITableViewDataSource class. Just return an array of strings and the subclass takes care of the rest.
class TableViewDataSource : UITableViewDataSource
{
public override string[] SectionIndexTitles(UITableView tableView)
{
return new string[] { /*your string values */};
}
}
*just a hint for those of us using C# and Mono (.NET) to write iPhone apps. :)
Here's a simple solution in Swift, assuming you have your title headers in an array. If the title couldn't be found, it will return the previous index in the array.
func sectionIndexTitlesForTableView(tableView: UITableView) -> [String]? {
return "ABCDEFGHIJKLMNOPQRSTUVWXYZ".characters.flatMap{String($0)}
}
func tableView(tableView: UITableView, sectionForSectionIndexTitle title: String, atIndex index: Int) -> Int {
return self.headerTitles.filter{$0 <= title}.count - 1
}
Here is a modified version of Kyle's function that handles the case of clicking an index for which you do not have a string:
- (NSInteger)indexForFirstChar:(NSString *)character inArray:(NSArray *)array
{
char testChar = [character characterAtIndex:0];
__block int retIdx = 0;
__block int lastIdx = 0;
[array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
char firstChar = [obj characterAtIndex:0];
if (testChar == firstChar) {
retIdx = idx;
*stop = YES;
}
//if we overshot the target, just use whatever previous one was
if (testChar < firstChar) {
retIdx = lastIdx;
*stop = YES;
}
lastIdx = idx;
}];
return retIdx;
}
Implement the delegate methods -sectionIndexTitlesForTableView:
and -tableView:sectionForSectionIndexTitle:atIndex:
See the UITableViewDataSource documentation for more info.