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


147

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

默认的滚动行为是,遇到某个节时,该节标题保持锚定在导航栏下方,直到下一个节滚动到视图中为止。

我的问题是:我可以更改默认行为,以使节标题不停留在顶部,而是在导航栏下滚动浏览节的其余部分吗?

我缺少明显的东西吗?

谢谢。


1
那一定是这个网站上最好的书面问题之一!:) 谢谢。
Fattie

1
在此处检查实现此效果的正确方法stackoverflow.com/a/13735238/1880899
Sumit Anantwar

Answers:


175

我解决此问题的方法是contentOffset根据(extends )contentInset中的来调整,如下所示:UITableViewControllerDelegateUIScrollViewDelegate

- (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 2011年

4
谢谢!这是一个完美的解决方案。您是否有针对页脚的解决方案?
阮阮

12
此解决方案不能正确处理轻按菜单,应该将其滚动到列表顶部。
里德·埃利斯

如果您需要相同的行为以查看页脚视图,只需更改最后一行,如下所示:scrollView.contentInset = UIEdgeInsetsMake(0,0,sectionHeaderHeight,0)
alex

这似乎足够了:scrollView.contentInset = UIEdgeInsetsMake(scrollView.contentOffset.y> = 0?-scrollView.contentOffset.y:0,0,0,0);
MacMark

85

您还可以在顶部添加一个零行的节,只需将上一节的页脚用作下一节的页眉即可。


13
就我而言,我有2层以上的部分,页脚将锚在底部..
约瑟夫·林

3
这很有创意,但是如果您使用NSFetchedResultsController加载表,则需要调整indexPath。
XJones 2011年

迄今为止+1最佳解决方案-让我像3行代码一样实施!
2012年

Brilliant Brilliant Brilliant :-)
Iphonic

这对于锚定的页脚将如何工作?希望能有所帮助!谢谢
darwindeeds

36

如果是我这样做,我将利用一个事实,即Plain风格的UITableViews具有粘性标头,而Grouped风格的UITableViews没有标头。我可能至少会尝试使用自定义表格单元格来模拟分组表格中普通单元格的外观。

我实际上没有尝试过,因此可能无法正常工作,但这就是我建议的做法。


可能会起作用,但是已经尝试过了吗?@voidStern的答案完美工作时,似乎需要进行大量工作!
bentford 2011年

当我有两个以上的部分时,voidStern的答案对我不起作用。Colin的答案更简单/更优雅,而无需手动移位部分编号和添加部分计数。
林书豪

提示:用于tableView.separatorColor = [UIColor clearColor];模仿纯表格视图。
林书豪

3
我尝试执行此操作,但是当我无法使组合的表格单元格看起来像普通的表格单元格时,即删除单元格左右两侧的边距。你是怎么做的?
亚当·卡特

1
非常感谢您对UITableViewStyle进行了明确的说明,即分组和普通,它决定了tableview的节页眉/页脚是否发粘。非常感谢@Colin Barrett
Dhaval H. Nena

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 2012年

为什么?您可以对版块标题进行相同的操作。只需记住nil代表奇数,标题则代表偶数。
LocoMike 2012年

1
是的-这种方法效果很好。记账很多(可能与我必须获得章节标题的方式有关),但它确实起作用。诀窍是numberOfRowsInSection和cellForRowAtIndexPath使用if(section%2!= 0 && section!= 0),而titleForHeaderInSection和其他部分索引函数使用if(section%2 == 0)
roocell 2012年

发起者应选择此作为答案。如果有一种方法可以子类化uitableview来自动锚定节标题,那就太好了。
roocell 2012年

这对我来说效果最好。我先尝试了@Colin的解决方案,但是在UITableView我使用的高度定制化的情况下,它不能很好地工作。
kubi 2012年

15

要以非骇客的方式解决此问题,需要完成几件事:

  1. 将表格视图样式设置为 UITableViewStyleGrouped
  2. 将表格视图设置backgroundColor[UIColor clearColor]
  3. backgroundView每个表格视图单元格上的设置为空视图backgroundColor [UIColor clearColor]
  4. 如有必要,请rowHeight适当地设置表格视图,tableView:heightForRowAtIndexPath:如果各个行的高度不同,则将其覆盖。

(+1)@Neil,我尝试了您的答案,但不幸的是,UITableViewStyleGrouped由于表格单元格的多余边距会改变其与标题的对齐方式。当您实际上想要描绘一个多列表并使用标题显示列标题(然后使各个表条目与这些标题对齐)时,这是一个问题。
brainjam 2011年

15

最初发布在这里,是使用IB的快速解决方案。尽管可以很简单地通过编程来完成。

实现这一目标的一种可能更简单的方法(使用IB):

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

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

有人评论说此解决方案隐藏了第一个标头,但是我没有注意到任何此类问题。它对我来说非常有效,并且是迄今为止我所见过的最简单的解决方案。


请注意,您可能应该将视图设置为使用clearcolor的背景色,否则,如果滚动到顶部,则会在反弹中看到白色。
Doc

3
它确实隐藏了我的第一个标题
Leena

Leena,您正在运行哪个版本的iOS?视图是UITableViewController还是其中包含UITableView的UIView?我不确定为什么对于某些人来说此解决方案隐藏了第一个标头,所以我尝试查找任何差异。
Doc

这个解决方案是完美的。我正在寻找无限的顶部和底部单元格背景外观-完全可以做到这一点。真好!
Taylor Halliday 2013年

这个解决方案非常好。比该线程中的其他答案更容易实现。我认为,这应该是公认的答案。
约翰·罗杰斯

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 2016年

完善。如果您的表格视图已经具有标题,只需将其高度增加偏移量,然后增加标题内容的顶部约束常量,即可将内容向下移动至原始位置
Jason

14

只需更改TableView样式:

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

UITableViewStyle文档

UITableViewStylePlain-纯表格视图。滚动表格视图时,所有节的页眉或页脚都将显示为行内分隔符并浮动。

UITableViewStyleGrouped-一个表视图,其部分显示不同的行组。节的页眉和页脚不浮动。


很好的答案,为我工作。无需任何处理
Shams Ahmed 2015年

8

选择分组的TableView样式

从情节提要中的tableView的“属性”检查器中选择“分组的Table View”样式。


5

使用透明视图设置表的headerView,并在剖面视图中设置标题的高度。还要使用-height框架初始化tableview。

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];

4

我找到了一个替代解决方案,使用每个节的第一个单元格代替真实的标题节,该解决方案看起来不太干净,但效果很好,可以为标题节使用已定义的原型单元格,并在cellForRowAtIndexPath方法中要求indexPath.row == 0,如果为true,则使用标头节原型单元格,否则使用默认原型单元格。


如果仍要使用indexPath.rowindexPath.section,请确保返回高度为0.0ffor,- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section以便标题不可见。

4

更改您的TableView样式:

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

根据UITableView的Apple文档:

UITableViewStylePlain-纯表格视图。滚动表格视图时,所有节的页眉或页脚都将显示为行内分隔符并浮动。

UITableViewStyleGrouped-一个表视图,其部分显示不同的行组。节的页眉和页脚不浮动。希望这个小的改变对您有帮助。


2

现在,分组样式看上去与iOS 7中的普通样式基本相同(就平坦度和背景而言),对我们而言,最好,最简单(即,至少不hacky)的解决方案是将表格视图的样式更改为分组。当我们在顶部集成可滚动导航栏时,使用contentInsets顶住始终是一个问题。使用分组的表格视图样式,它看起来完全相同(使用我们的单元格),并且节标题保持固定。没有滚动怪异。


这是最好的解决方案,特别是如果单元格是自定义的,则似乎只是在滚动到达顶部时停止节标题的“保持”。
再生钢

1

为您的tableView分配一个负插图。如果您有22px高的节头,并且您不希望它们很粘,请在reloadData之后立即添加:

self.tableView.contentInset = UIEdgeInsetsMake(-22, 0, 0, 0); 
self.tableView.contentSize = CGSizeMake(self.tableView.contentSize.width, self.tableView.contentSize.height+22); 

对我来说就像一个魅力。同样适用于部分页脚,只需在底部分配负插图即可。


这只是砍掉第一个标题?
cannyboy

完美地为我工作!谢谢
丹尼尔(Daniel)



0

@LocoMike的答案最适合我的tableView,但是在使用页脚时也坏了。因此,这是使用页眉和页脚时的更正解决方案:

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return (self.sections.count + 1) * 3;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    if (section % 3 != 1) {
        return 0;
    }
    section = section / 3;
    ...
}

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
    if (section % 3 != 0) {
        return nil;
    }
    section = section / 3;
    ...
}

- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
    if (section % 3 != 0) {
        return 0;
    }
    section = section / 3;
    ...
}

- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section
{
    if (section % 3 != 2) {
        return 0;
    }
    section = section / 3;
    ...
}

- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
    if (section % 3 != 0) {
        return nil;
    }
    section = section / 3;
    ...
}

- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section
{
    if (section % 3 != 2) {
        return nil;
    }
    section = section / 3;
    ...
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    int section = indexPath.section;
    section = section / 3;
    ...
}

0

@awulf答案的Swift版本,效果很好!

func scrollViewDidScroll(scrollView: UIScrollView) {
    let sectionHeight: CGFloat = 80
    if scrollView.contentOffset.y <= sectionHeight {
        scrollView.contentInset = UIEdgeInsetsMake( -scrollView.contentOffset.y, 0, 0, 0)
    }else if scrollView.contentOffset.y >= sectionHeight {
        scrollView.contentInset = UIEdgeInsetsMake(-sectionHeight, 0, 0, 0)
    }
}

-2

我了解到,只需设置tableHeaderView属性即可,即:

 tableView.tableHeaderView = customView;

就是这样。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.