【发布时间】:2025-12-13 02:00:02
【问题描述】:
在 iPhone 音乐应用程序中,选择 Artist、Songs 或 Albums 会在 UI 右侧显示一个 tableView,其中包含单个字母的垂直列表,可以快速滚动。如何在我的应用中启用此功能?
干杯, 道格
【问题讨论】:
标签: ios iphone objective-c uitableview
在 iPhone 音乐应用程序中,选择 Artist、Songs 或 Albums 会在 UI 右侧显示一个 tableView,其中包含单个字母的垂直列表,可以快速滚动。如何在我的应用中启用此功能?
干杯, 道格
【问题讨论】:
标签: ios iphone objective-c uitableview
实现委托方法-sectionIndexTitlesForTableView:和-tableView:sectionForSectionIndexTitle:atIndex:
有关详细信息,请参阅UITableViewDataSource 文档。
【讨论】:
提供您自己的索引字符:
- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView {
return[NSArray arrayWithObjects:@"a", @"e", @"i", @"m", @"p", nil];
}
然后:
- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString
*)title atIndex:(NSInteger)index {
return <yourSectionIndexForTheSectionForSectionIndexTitle >;
}
您将需要部分。
【讨论】:
如果您使用的是 MonoTouch,请覆盖 UITableViewDataSource 类中的 SectionIndexTitles(UITableView) 方法。只需返回一个字符串数组,其余的由子类处理。
class TableViewDataSource : UITableViewDataSource
{
public override string[] SectionIndexTitles(UITableView tableView)
{
return new string[] { /*your string values */};
}
}
*只是对我们这些使用 C# 和 Mono (.NET) 编写 iPhone 应用程序的人的提示。 :)
【讨论】:
一群人问是否可以不使用部分来做到这一点。我想要同样的东西,我找到了一个可能有点阴暗的解决方案,并且不会向 sectionForSectionIndexTitle 返回值但是如果你在一个角落并且不想为字母表的每个字母创建一个部分是肯定的解决方法。提前对任何代码纳粹表示抱歉。 :P
- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView {
if (thisTableDataIsShowing)
{
NSMutableArray *charactersForSort = [[NSMutableArray alloc] init];
for (NSDictionary *item in d_itemsInTable)
{
if (![charactersForSort containsObject:[[item valueForKey:@"character_field_to_sort_by"] substringToIndex:1]])
{
[charactersForSort addObject:[[item valueForKey:@"character_field_to_sort_by"] substringToIndex:1]];
}
}
return charactersForSort;
}
return nil;
}
- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index {
BOOL found = NO;
NSInteger b = 0;
for (NSDictionary *item in d_itemsInTable)
{
if ([[[item valueForKey:@"character_field_to_sort_by"] substringToIndex:1] isEqualToString:title])
if (!found)
{
[d_yourTableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:b inSection:0] atScrollPosition:UITableViewScrollPositionTop animated:NO];
found = YES;
}
b++;
}
}
如果您要获取大量数据并对其进行分段将需要大量工作,则效果很好。 :) 尝试使用泛型变量,所以你知道我在做什么。 d_itemsInTable 是我在 UITableView 中列出的 NSDictionaries 的 NSArray。
【讨论】:
您必须考虑的其他事情是将每种语言的部分本地化。经过一番挖掘,我发现UILocalizedIndexedCollation 非常有用:
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
return [[[UILocalizedIndexedCollation currentCollation] sectionTitles] objectAtIndex:section];
}
- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView {
return [[UILocalizedIndexedCollation currentCollation] sectionIndexTitles];
}
- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index {
return [[UILocalizedIndexedCollation currentCollation] sectionForSectionIndexTitleAtIndex:index];
}
https://developer.apple.com/documentation/uikit/uilocalizedindexedcollation
【讨论】:
我想出了一种替代方法来处理单个字母列表而不使用部分。它类似于 Zaph 的答案,但不是从返回新索引中获取任何值(因为我们总是有 1 个部分),我们计算数组中以某个字符开头的第一项的位置的索引,然后滚动给它。
缺点是这需要每次都搜索数组(这绝对可怕吗?),但是我没有注意到 iOS 模拟器或我的 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;
}
添加属性以存储上次选择的索引,如
@property (assign, nonatomic) NSInteger previousSearchIndex;
并且每次都存储这个属性:
- (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;
}
并更新scrollToRow 代码如下:
[tableView scrollToRowAtIndexPath:newIndexPath atScrollPosition:UITableViewScrollPositionTop animated:YES];
用漂亮的动画做得更好。
【讨论】:
这是 Kyle 函数的修改版本,用于处理单击没有字符串的索引的情况:
- (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;
}
【讨论】:
如果您使用的是NSFetchedResultsController,您可以这样做:
- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView {
return [frc sectionIndexTitles];
}
- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index {
return [frc sectionForSectionIndexTitle:title atIndex:index];
}
【讨论】:
这是 Swift 中的一个简单解决方案,假设您将标题标题放在一个数组中。如果找不到标题,它将返回数组中的前一个索引。
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
}
【讨论】:
return self.headerTitles.count - self.headerTitles.filter{$0 > title}.count 以返回给定索引标题的第一个而不是最后一个匹配部分的索引。