【问题标题】:iPhone UITableView. How do turn on the single letter alphabetical list like the Music App?iPhone UITableView。如何开启音乐 App 之类的单字母字母列表?
【发布时间】:2025-12-13 02:00:02
【问题描述】:

在 iPhone 音乐应用程序中,选择 Artist、Songs 或 Albums 会在 UI 右侧显示一个 tableView,其中包含单个字母的垂直列表,可以快速滚动。如何在我的应用中启用此功能?

干杯, 道格

【问题讨论】:

    标签: ios iphone objective-c uitableview


    【解决方案1】:

    实现委托方法-sectionIndexTitlesForTableView:-tableView:sectionForSectionIndexTitle:atIndex:

    有关详细信息,请参阅UITableViewDataSource 文档。

    【讨论】:

      【解决方案2】:

      提供您自己的索引字符:

      - (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 >;
      }
      

      您将需要部分。

      【讨论】:

      • 有没有办法在没有节的情况下拥有索引(对于平面列表)?
      • @ThEuSeFuL 您可以有部分而不显示部分标题。对于最终用户来说,这看起来像是一个平面列表。
      • “yourSectionIndexForTheSectionForSectionIndexTitle”是什么意思?
      • @ravoorinandan 他本质上是指在您的titles 数组中返回title 变量的索引。所以如果标题是@"a",它应该返回0。如果标题是@"i",它应该返回2,以此类推。您可以通过使数组对此处显示的两种方法都可用,然后在第二种方法中调用 [array indexOfObject: title] 来做到这一点。虽然我会说下面的答案(与 UILocalizedIndexedCollat​​ion 相关)可能更好。
      • 以防万一,有人犯了和我一样的错误,选择故事板中的 tableView 并将文本颜色更改为您想要的颜色。在我的情况下,文本颜色被自动选择为清晰颜色,因此索引没有列出。我浪费了很多时间来找出问题所在。
      【解决方案3】:

      如果您使用的是 MonoTouch,请覆盖 UITableViewDataSource 类中的 SectionIndexTitles(UITableView) 方法。只需返回一个字符串数组,其余的由子类处理。

      class TableViewDataSource : UITableViewDataSource
      {
        public override string[] SectionIndexTitles(UITableView tableView) 
        { 
          return new string[] { /*your string values */};
        }
      }
      

      *只是对我们这些使用 C# 和 Mono (.NET) 编写 iPhone 应用程序的人的提示。 :)

      【讨论】:

        【解决方案4】:

        一群人问是否可以不使用部分来做到这一点。我想要同样的东西,我找到了一个可能有点阴暗的解决方案,并且不会向 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。

        【讨论】:

        • 有趣的传真:如果返回 -1,它将忽略编译器警告,并且不会尝试从 sectionForSectionIndexTitle 方法滚动到该部分
        • 非常棒的样本感谢分配可能在fund=YES之后添加一个刹车;也不会那么糟糕^^
        【解决方案5】:

        您必须考虑的其他事情是将每种语言的部分本地化。经过一番挖掘,我发现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

        【讨论】:

          【解决方案6】:

          我想出了一种替代方法来处理单个字母列表而不使用部分。它类似于 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];
          

          用漂亮的动画做得更好。

          【讨论】:

          • 赞成只是因为这意味着我不必输入字母数组!
          • 嘿。这也不是一个糟糕的解决方案......苹果在我的代码示例中使用它接受了我的可可营。这是否意味着他们正式认可它......我不知道;)。
          • @PaulCezanne 实际上,为了使其更清晰,我建议您遍历字符串数组并获取所有字符串的大写首字母,这样您的列表是动态的,因为如果您不会有任何 Q您没有任何以字母 Q 开头的项目。如果您希望我更新代码以显示此内容,请告诉我。
          • 谢谢。我实际上是在我正在开发的另一个应用程序中做到这一点的。在实践中看起来很奇怪。侧面的 A-Z 阵列非常具有解释性。当您只有几个字母时,如果您只有少量歌曲或过滤了很多,就会有这种情况,旁边的字母的用途看起来很奇怪。
          • @Mingebag 尝试通过检查每次调用 indexForFirstChar 的返回值来调试它。每次应该在 0 到 25 之间。如果它 > 25,那么可能你有非字母字符或其他原因导致它返回过高的值。
          【解决方案7】:

          这是 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;
          }
          

          【讨论】:

            【解决方案8】:

            如果您使用的是NSFetchedResultsController,您可以这样做:

            - (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView {
                return [frc sectionIndexTitles];
            }
            
            - (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index {
                return [frc sectionForSectionIndexTitle:title atIndex:index];
            }
            

            【讨论】:

              【解决方案9】:

              这是 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 &gt; title}.count 以返回给定索引标题的第一个而不是最后一个匹配部分的索引。
              • 取决于客户的期望,真的。很有可能,您选择的任何功能都将与他们想要的相反。 :)