更改UITableView分区标题的默认滚动行为

154

我有一个带有两个部分的UITableView,它是一个简单的表格视图。我正在使用viewForHeaderInSection为这些标题创建自定义视图。到目前为止都很好。

默认的滚动行为是,当遇到一个部分时,部分标题会保持在Nav栏下方固定,直到下一个部分滚动到视图中。

我的问题是:我是否可以更改默认行为,使部分标题不会固定在顶部,而是随着其余部分行一起在导航栏下方滚动?

我是否忽略了一些明显的内容?

谢谢。


1
点击此处查看实现此效果的正确方式:https://dev59.com/NHNA5IYBdhLWcg3wI6V4#13735238 - anemo
19个回答

175

我解决这个问题的方法是在UITableViewControllerDelegate(扩展了UIScrollViewDelegate)中根据contentInset调整contentOffset,代码如下:

- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
       CGFloat sectionHeaderHeight = 40;
   if (scrollView.contentOffset.y<=sectionHeaderHeight&&scrollView.contentOffset.y>=0) {
       scrollView.contentInset = UIEdgeInsetsMake(-scrollView.contentOffset.y, 0, 0, 0);
   } else if (scrollView.contentOffset.y>=sectionHeaderHeight) {
       scrollView.contentInset = UIEdgeInsetsMake(-sectionHeaderHeight, 0, 0, 0);
   }
}

唯一的问题是当滚动回顶部时,它会略微失去回弹效果。


{注意: "40" 应该是您第 0 部分标题的确切高度。如果您使用的数字比您的第 0 部分标题高度更大,您将看到手指的反应受到影响(尝试使用 "1000",您将看到在顶部弹跳行为有点奇怪)。如果数字与您的第 0 部分标题高度相匹配,则手感似乎完美或接近完美。}


1
比上面的更完美的解决方案。 - prathumca
4
谢谢!这是完美的解决方案。你有这个小节页脚的解决方案吗? - Tuan Nguyen
12
这个解决方案无法正确处理菜单栏点击的情况,该情况应该会滚动到列表顶部。 - Reid Ellis
这个解决方案是我曾经参与的一个项目中的,但为什么段落标题不能固定在导航栏上,这让人感到很疯狂。这只是一个丑陋的hack,而不是真正的解决方案。很遗憾,当我需要这样的行为时,这是我能找到的最好的方法。 - trickster77777
非常好的解决方案,但不适用于 tableView.bounce = NO - nitish005
显示剩余11条评论

86

您还可以在顶部添加一个零行的区域,然后简单地使用上一部分的页脚作为下一部分的页眉。


13
在我的情况下,如果我有超过两个部分,页脚会停留在底部。 - Joseph Lin
3
这个方法很有创意,但如果你在使用 NSFetchedResultsController 来加载表格的话,需要调整 indexPath。 - XJones
这对于固定在底部的页脚怎么使用?需要一些帮助,谢谢。 - darwindeeds
伟大的解决方案,非常聪明。 - jonschneider
太棒了,两全其美!不仅解决了那个问题,还修复了被导航栏遮挡的页眉插图 ;) - euthimis87
显示剩余3条评论

36

如果是我来做这个,我会利用UITableView的Plain样式具有粘性表头的特点,而Grouped样式没有。我可能会尝试使用自定义的表格单元格来模仿Grouped表格中Plain单元格的外观。

我实际上还没有尝试过这个方法,所以不确定是否可行,但这就是我建议的做法。


3
我尝试过做这个,但当我无法使分组的表格单元格看起来像普通的单元格时,即在单元格两侧去掉左右边距时,你是如何做到的? - Adam Carter
这种方法对我很有效。唯一的问题是让单元格大小正确,所以在我的UITableViewCell子类中,我重写了setFrame:函数,将框架水平增加10px。即使最终单元格框架宽度最终为340px,在添加回分组单元格插图后,所有内部单元格视图都被正确绘制。以下是一些示例代码:-(void)setFrame:(CGRect)frame { CGFloat插入= 10.0f; frame.origin.x- =插入; frame.size.width+ = 2 *插入; [super setFrame:frame]; } - Mr. T
我知道这有点老了,但对于仍在看这个的任何人来说,这是最简单的解决方案。为了向这个答案添加一些代码,我只是在我的initWithStyle:方法中调用了self = [super initWithStyle:UITableViewStyleGrouped];,而不是通常的self = [super initWithStyle:style];。 - baptzmoffire
这本来是个好答案,但我的设计师很挑剔,想让某些标题固定,而其他的则不固定。真是个噩梦! - Flying_Banana
1
非常感谢您对UITableViewStyle即分组和纯样式进行了清晰的解释,这决定了TableView的节标题/脚注是否粘性。非常感谢@Colin Barrett。 - Dhaval H. Nena
显示剩余6条评论

28

虽然有些晚,但我发现了最终的解决方案!

你想要做的是,如果你有10个章节,就让dataSource返回20个。使用偶数作为章节标题,奇数作为章节内容。就像这样:

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    if (section%2 == 0) {
        return 0;
    }else {
        return 5;
    }
}

-(NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
    if (section%2 == 0) {
        return [NSString stringWithFormat:@"%i", section+1];
    }else {
        return nil;
    }
}

哇! :D


很好的想法。但是如果您有章节索引标题,我怀疑这将会使它们混乱。 - roocell
为什么?你可以用相同的方法翻译章节标题。只需记住奇数使用“nil”,偶数使用标题。 - LocoMike
1
是的 - 这个方法非常有效。它需要很多簿记(可能与我获取章节标题的方式有关),但它起作用了。诀窍在于numberOfRowsInSection和cellForRowAtIndexPath使用if (section%2!= 0 && section!= 0),而titleForHeaderInSection和其他章节索引函数使用if (section%2 == 0)。 - roocell
如果有一种方法可以子类化UITableView以自动锚定部分标题,那就太好了。 - roocell
这对我来说效果最好。我先尝试了@Colin的解决方案,但它在我使用的高度定制的UITableView上效果不佳。 - kubi

16

这是一个关于编程的快速解决方案,最初在这里发布,可以使用IB(Interface Builder)来实现。虽然也可以通过编程轻松地完成相同的操作。

一种可能更简单的方法(使用IB):

将UIView拖动到您的TableView上,以使其成为标题视图。

  1. 将该标题视图高度设置为100px
  2. 将tableview contentInset(顶部)设置为-100
  3. 现在,部分标题将像任何常规单元格一样滚动。

有些人评论说,这种解决方案会隐藏第一个标题,但我没有注意到任何此类问题。对我来说,它完美地工作,并且是迄今为止我看到的最简单的解决方案。


请注意,您应该将视图设置为使用clearcolor背景颜色,否则如果您滚动到顶部之外,您会看到弹跳区域中的白色。 - Doc
3
它确实隐藏了我的第一个标题。 - Leena
Leena,你的iOS版本是什么?视图是UITableViewController还是包含UITableView的UIView?我不确定为什么对于某些人来说这个解决方案会隐藏第一个标题,所以我正在尝试找到任何差异。 - Doc
这个解决方案非常完美。我想要一个无限的顶部和底部单元格背景外观 - 这完全实现了它。不错! - Taylor Halliday
这个解决方案非常出色。比本帖中其他答案更容易实现。在我看来,这应该是被采纳的答案。 - John Rogers

15

解决这个问题需要完成几件事情,而且要以非 hacky 的方式完成:

  1. 将表格视图样式设置为 UITableViewStyleGrouped
  2. 将表格视图的背景颜色设置为 [UIColor clearColor]
  3. 将每个表格视图单元格的 backgroundView 设置为空视图,并设置其背景颜色为 [UIColor clearColor]
  4. 如有必要,适当设置表格视图的行高,或者如果单个行高不同,则重写 tableView:heightForRowAtIndexPath:

(+1) @Neil,我尝试了你的答案,但是很遗憾,对于UITableViewStyleGrouped,表格单元格有额外的边距,这会改变它们与标题的对齐方式。当您想要描绘一个多列表并使用标题显示列标题(然后使各个表条目与这些标题对齐)时,这是一个问题。 - brainjam

15
我对迄今为止在这里描述的解决方案不满意,所以我尝试将它们结合起来。下面的代码是由@awulf和@cescofry启发而来的。它适用于我,因为我没有真正的表视图头。如果您已经有一个表视图头,您可能需要调整高度。
// Set the edge inset
self.tableView.contentInset = UIEdgeInsetsMake(-23.0f, 0, 0, 0);

// Add a transparent UIView with the height of the section header (ARC enabled)
[self.tableView setTableHeaderView:[[UIView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 100.0f, 23.0f)]];

这是我找到的最简单的解决方案。 - Zorayr
这对我在iOS9.2上有效,所以即使是一个旧的答案,它仍然是有效的。并且它没有隐藏第一个部分标题。它能工作并且完美地工作。 - idStar
太好了。如果你的表格视图已经有一个头部,则只需通过偏移增加其高度,然后增加头部内容的顶部约束常量以将内容向下移动到原始位置即可。 - Jason

15

仅更改TableView样式:

self.tableview = [[UITableView alloc] initwithFrame:frame style:UITableViewStyleGrouped];

UITableViewStyle 文档:

UITableViewStylePlain- 普通的表格视图。任何部分的头部或尾部都会显示为内联分隔符,并在滚动表格视图时浮动。

UITableViewStyleGrouped- 表示具有不同行组的明显组的表格视图。节标题和页脚不会漂浮。


很棒的答案,对我有用。不需要任何变通方法。 - Shams Ahmed

11

在 storyboard 中的 tableView 的属性检查器中选择“分组表格视图”样式。

选择分组表格视图样式


5

使用与 section header 高度相同的透明视图来设置表格的表头视图(headerView)。同样,将tableview的y frame初始化为-height。

self.tableview = [[UITableView alloc] initWithFrame:CGRectMake(0, - height, 300, 400)];

UIView *headerView = [[[UIView alloc] initWithFrame:CGRectMake(0, 0, width, height)] autorelease];
[self.tableView setTableHeaderView:headerView];

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接