当 UITableView 有一百来行时,它就变得有些笨重了。如果用户为了找到他们想要的东西,像玩水果忍者的猫那样疯狂地滑动屏幕时,你可能会想要重新考虑一下用户界面的展现方式。
那么,你可以做些什么呢?
首先,你可以按层级的方式组织你的数据,基于层级的分支数,这种方式可以很明显地减少每个节目上的行数。
同时,你也可以在列表上方加个 UISearchBar
,允许用户根据关键字过滤,从而找到他们想要的东西(或者,也许更重要的是,看他们想要找的东西在不在列表里)。
还有第三种在 iOS 应用中并没有被很好利用的办法:区域索引标题(section index titles)。它们是在列表右边纵向排列的字母,你可以在电话本联系人界面和音乐曲库界面中看到它们。
当用户在那个列表里向下移动手指时,列表会在对应的区域间跳动。这会使得冗长的列表视图变得超级好用。
可以通过实现下列 UITableViewDataSource
中的方法来显示区域索引标题:
-sectionIndexTitlesForTableView:
—— 返回一个区域索引标题的数组,用于在列表右边显示,例如字母序列 A…Z 和 #。区域索引标题很短,通常不能多于两个 Unicode 字符。
-tableView:sectionForSectionIndexTitle:atIndex:
—— 返回当用户触摸到某个索引标题时列表应该跳至的区域的索引。
NSHipster 的老读者可能已经猜到了,我们肯定不想自己去生成这个字母列表。对于不同的地区来说,字母的顺序,甚至「字母」,的意义都会大不相同。
UILocalizedIndexedCollation
来拯救我们了。
UILocalizedIndexedCollation
是一个帮助我们组织列表数据的类,它能够根据地区来生成与之对应区域索引标题。不需要直接创建它的对象,我们可以通过 UILocalizedIndexedCollation +currentCollation
获得一个对应当前地区的单例对象。
UILocalizedIndexedCollation
的首要任务就是决定对于当前地区区域索引标题应该是什么,我们可以通过 sectionIndexTitles
属性来获得它们。
下表可以帮助你更好的了解不同地区之间区域索引标题的差别。
如果你自己想要看这些的话,你需要把对应的地区加入到你的项目本地化列表中。
Locale | Section Index Titles |
---|---|
en_US | 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, # |
ja_JP | 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, あ, か, さ, た, な, は, ま, や, ら, わ, # |
sv_SE | 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, Å, Ä, Ö, # |
ko_KO | 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, ㄱ, ㄴ, ㄷ, ㄹ, ㅁ, ㅂ, ㅅ, ㅇ, ㅈ, ㅊ, ㅋ, ㅌ, ㅍ, ㅎ, # |
ar_SA | 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, آ, ب, ت, ث, ج, ح, خ, د, ذ, ر, ز, س, ش, ص, ض, ط, ظ, ع, غ, ف, ق, ك, ل, م, ن, ه, و, ي, # |
你难道不为不用自己做这些事情而高兴吗?
有了你面前的这些区域标题,下一步就是判断每个模型对象分别对应哪个区域了。这可以通过实现 -sectionForObject:collationStringSelector:
做到。这个方法返回 NSInteger
类型的索引,它对应了模型对象的指定方法的返回值。方法名称可以为 localizedName
、title
甚至 description
等。
显而易见,列表数据源中会有一个数组,它对应了列表中有多少区域,数组元素表示区域中的每一行。由于整理工作是由 UILocalizedIndexedCollation
来做的,因此理所当然地,也应该由它来为每个区域中的行进行排序。和 -sectionForObject:collationStringSelector:
的实现方式类似,– sortedArrayFromArray:collationStringSelector:
可以为我们基于模型对象的本地化标题来排列模型对象。
最后,数据源应该实现 -tableView:sectionForSectionIndexTitle:atIndex:
方法,这样当我们触摸到区域索引标题时,能够让列表调至对应的区域。UILocalizedIndexedCollation -sectionForSectionIndexTitleAtIndex:
可以轻松帮我们做到。
都说完了,下边是列表数据源的一个常见实现:
- (void)setObjects:(NSArray *)objects {
SEL selector = @selector(localizedTitle);
NSInteger index, sectionTitlesCount = [[[UILocalizedIndexedCollation currentCollation] sectionTitles] count];
NSMutableArray *mutableSections = [[NSMutableArray alloc] initWithCapacity:sectionTitlesCount];
for (NSUInteger idx = 0; idx < sectionTitlesCount; idx++) {
[mutableSections addObject:[NSMutableArray array]];
}
for (id object in objects) {
NSInteger sectionNumber = [[UILocalizedIndexedCollation currentCollation] sectionForObject:object collationStringSelector:selector];
[[mutableSections objectAtIndex:sectionNumber] addObject:object];
}
for (NSUInteger idx = 0; idx < sectionTitlesCount; idx++) {
NSArray *objectsForSection = [mutableSections objectAtIndex:idx];
[mutableSections replaceObjectAtIndex:idx withObject:[[UILocalizedIndexedCollation currentCollation] sortedArrayFromArray:objectsForSection collationStringSelector:selector]];
}
self.sections = mutableSections;
[self.tableView reloadData];
}
- (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];
}
有一个特殊的区域索引标题需要提一下:UITableViewIndexSearch
。列表中一般同时会有搜索框和区域索引。为了方便同时也保持视觉上的一致性,通常第一个区域索引处会放个搜索图标,当你触摸这个图标时,列表会滑至顶部的搜索框区域。
为了在列表右边可以看到搜索图标,你需要把 UITableViewIndexSearch
这个 NSString
常量插入到 -sectionIndexTitlesForTableView:
返回值的前边,并且调整 -tableView:sectionForSectionIndexTitle:atIndex:
使得它返回正确的区域索引。
请所有的 NSHipsters 记住:如果你看到了一个超长的列表,那就一把火把它烧掉!
……其实是说,要用层级、搜索框以及区域索引标题来改变展现方式。当你要实现区域索引标题时,可以用 UILocalizedIndexedCollation
来帮你。
我们都这样做了之后,那就能够摆脱因滑动超长列表而带来的压力,从而可以花更多的时间享受更美好的事情,比如看些宠物玩 iPad 的视频。
除非另有声明,本文采用知识共享「署名-非商业性使用 3.0 中国大陆」许可协议授权。