是否可以使用UITableViewStylePlain禁用UITableView中的浮动标头?


177

我正在使用UITableView来布局内容“页面”。我正在使用表格视图的标题来布局某些图像等,如果它们不浮动但仍保持静态(如将样式设置为)一样,则希望使用它UITableViewStyleGrouped

UITableViewStyleGrouped除了使用之外,还有其他方法吗?我想避免使用分组,因为它会在我所有的单元格下方添加边距,并且需要为每个单元格禁用背景视图。我想完全控制自己的布局。理想情况下,它们应该是“ UITableViewStyleBareBones”,但我在文档中没有看到该选项...

非常感谢,



59
使用表格样式UITableViewStyleGrouped-这是所有正在寻找禁用浮动标题并且不阅读整个问题的答案(这是我发生的事情...)。
平均乔

@Kasztan这是一个很好的零线解决方案!谢谢。
Lee Fastenau 2015年

Answers:


27

您应该能够通过使用自定义单元格进行标头行伪造。然后,它们将像表视图中的任何其他单元格一样滚动。

您只需要在您的逻辑中添加一些逻辑 cellForRowAtIndexPath标题行中以返回正确的单元格类型。

不过,您可能必须自己管理自己的部分,即将所有内容都放在一个部分中并伪造标题。(您也可以尝试为标题视图返回隐藏视图,但我不知道这样是否可行)


11
是的...虽然看起来有点骇人听闻。这似乎是Apple的一部分,这是疏忽大意,因为显然有代码可以做到,所以我们只需要设置一个标志即可。
Tricky

2
在下面检查我的答案以获得正确的方法,而无需任何特殊的修改,只需将每个节的第一个单元格作为其节头管理即可!
Sumit Anantwar

338

可能更简单的方法来实现此目的:

目标C:

CGFloat dummyViewHeight = 40;
UIView *dummyView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.tableView.bounds.size.width, dummyViewHeight)];
self.tableView.tableHeaderView = dummyView;
self.tableView.contentInset = UIEdgeInsetsMake(-dummyViewHeight, 0, 0, 0);

迅速:

let dummyViewHeight = CGFloat(40)
self.tableView.tableHeaderView = UIView(frame: CGRect(x: 0, y: 0, width: self.tableView.bounds.size.width, height: dummyViewHeight))
self.tableView.contentInset = UIEdgeInsetsMake(-dummyViewHeight, 0, 0, 0)

现在,节标题将像任何常规单元格一样滚动。


5
这种friggin就像一个魅力!表标题视图标题的存在阻止了节标题的浮动。一个带有适当内容插入表的不可见标题可以解决问题..非常感谢!想知道为什么这个答案没有引起足够的重视
Shyam Bhat 2012年

4
还是不相信!没有私有API,没有深入研究类,什么都没有!我已经面对这个问题了很长时间,并且一直在滚动视图的顶部添加表格视图(禁用了表格视图滚动),这会使表格标题正常滚动。但是最近才开始寻找苹果公司是否提供了任何新方法来解决此问题。。很高兴我偶然发现了您的答案。再次感谢
Shyam Bhat 2012年

13
我看到的唯一缺点是不再显示顶部标题(您只能通过向上滚动才能看到它)。至少对我来说没有显示
Ruzard

5
您仍然可以通过增加表顶部的高度(等于部分标题的高度)并减少相同数量的内容插图来显示表的tableHeaderView。
Shyam Bhat

2
我希望我可以再次投票赞成,我敢肯定,这至少是我第三次结束这个问题,这已经解决了我的问题。
Ell Neal

250

(对于那些由于错误的表格样式而到达这里的人),可以通过属性检查器或通过代码将表格样式从纯样式更改为分组样式:

let tableView = UITableView(frame: .zero, style: .grouped)

3
这正是我想要的。谢谢!
ninjudd 2014年

4
“然后再使用UITableViewStyleGrouped,有没有办法做到这一点?”
迈克尔·彼得森

26
好东西,我学会了在实施最受欢迎的方法之前先检查所有答案:)
Tung Fam

1
现在这些家伙,就是您想要的答案。请注意,您也可以TableView Style在AttributesInspector中找到该属性。
Cristian Holdunu

4
该问题专门提到寻找一种方法“其他然后使用UITableViewStyleGrouped”
Ferdz

73

警告:此解决方案实现了保留的API方法。这可能会阻止该应用程序被Apple批准在AppStore上分发。

我已经描述了一些私有方法,这些私有方法会在我的博客中浮动部分标题

基本上,您只需要子类化UITableView并返回NO其两个方法即可:

- (BOOL)allowsHeaderViewsToFloat;
- (BOOL)allowsFooterViewsToFloat;

40
答案不应该仅仅因为它引用了私有API(而不是每个人都在编写要提交给AppStore的代码)就被接受。当答案碰巧是正确的时,这是双重事实。为这些方法返回NO的子类化(或更容易地,使它成为UITableView类别)阻止了标头的浮动。如果您希望将其添加为公共API,请提交错误报告:bugreport.apple.com
jemmons

5
如果我们在App中实现该功能,Apple怎么会意识到您正在使用私有API?据他们所知,我只是在UITableView的子类中实现了一个方法,如果不对代码进行反汇编,他们甚至看不到这一点,这并不容易。这不是调用私有API的方法,它只是实现一个....
哈维尔·索托

3
@jemmons,如果您不希望我拒绝您的回答,请将其标记为“此处使用私有API”。
有点

2
您的“我的博客”链接已断开
MartinMoizard 2014年

12
@jemmons“不是每个人都在编写要提交给AppStore的代码” Appstore并不是真正的问题。问题是苹果可以更改私有API,而无需进行通信或弃用,这将破坏您的应用程序。这是为什么您不应该使用私有API而不是被苹果拒绝的更好理由
aryaxt 2015年

29

好的,我知道已经晚了,但是我必须这样做。现在,我已经花了10个小时来寻找有效的解决方案,但没有找到完整的答案。确实发现了一些提示,但初学者很难理解。因此,我必须投入2美分并完成答案。

正如在少数几个答案中所建议的那样,我能够实现的唯一可行的解​​决方案是在表格视图中插入普通单元格并将它们作为节标题处理,但是更好的方法是在这些表格中插入这些单元格每个部分的第0行。这样,我们可以非常轻松地处理这些自定义的非浮动标头。

所以,步骤是。

  1. 使用UITableViewStylePlain样式实现UITableView。

    -(void) loadView
    {
        [super loadView];
    
        UITableView *tblView =[[UITableView alloc] initWithFrame:CGRectMake(0, frame.origin.y, frame.size.width, frame.size.height-44-61-frame.origin.y) style:UITableViewStylePlain];
        tblView.delegate=self;
        tblView.dataSource=self;
        tblView.tag=2;
        tblView.backgroundColor=[UIColor clearColor];
        tblView.separatorStyle = UITableViewCellSeparatorStyleNone;
    }
  2. 照常实现titleForHeaderInSection(您可以使用自己的逻辑来获取此值,但我更喜欢使用标准委托)。

    - (NSString *)tableView: (UITableView *)tableView titleForHeaderInSection:(NSInteger)section
    {
        NSString *headerTitle = [sectionArray objectAtIndex:section];
        return headerTitle;
    }
  3. 照例实现numberOfSectionsInTableView

    - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView 
    {
        int sectionCount = [sectionArray count];
        return sectionCount;
    }
  4. 照常实现numberOfRowsInSection。

    - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 
    {
        int rowCount = [[cellArray objectAtIndex:section] count];
        return rowCount +1; //+1 for the extra row which we will fake for the Section Header
    }
  5. 在heightForHeaderInSection中返回0.0f。

    - (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
    {
        return 0.0f;
    }
  6. 不要实现viewForHeaderInSection。完全删除该方法,而不是返回nil。

  7. 在heightForRowAtIndexPath中。检查if(indexpath.row == 0)并返回节标题的所需单元格高度,否则返回该单元格的高度。

    - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        if(indexPath.row == 0)
        {
            return 80; //Height for the section header
        }
        else
        {
            return 70; //Height for the normal cell
        }
    }
  8. 现在在cellForRowAtIndexPath中,检查if(indexpath.row == 0)并按您希望的部分标头实现单元格并将选择样式设置为none。ELSE像您希望普通单元格那样实现单元格。

    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        if (indexPath.row == 0)
        {
            UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"SectionCell"];
            if (cell == nil)
            {
                cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"SectionCell"] autorelease];
                cell.selectionStyle = UITableViewCellSelectionStyleNone; //So that the section header does not appear selected
    
                cell.backgroundView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"SectionHeaderBackground"]];
            }
    
            cell.textLabel.text = [tableView.dataSource tableView:tableView titleForHeaderInSection:indexPath.section];
    
            return cell;
        }
        else
        {
            UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell"];
    
            if (cell == nil) 
            {
                cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"Cell"] autorelease];
                cell.selectionStyle = UITableViewCellSelectionStyleGray; //So that the normal cell looks selected
    
                cell.backgroundView =[[[UIImageView alloc] initWithImage:[UIImage imageNamed:@"CellBackground"]]autorelease];
                cell.selectedBackgroundView=[[[UIImageView alloc] initWithImage:[UIImage imageNamed:@"SelectedCellBackground"]] autorelease];
            }
    
            cell.textLabel.text = [[cellArray objectAtIndex:indexPath.section] objectAtIndex:indexPath.row -1]; //row -1 to compensate for the extra header row
    
            return cell;
        }
    }
  9. 现在实现willSelectRowAtIndexPath,如果indexpath.row == 0,则返回nil。这将确保didSelectRowAtIndexPath永远不会为Section标头行触发。

    - (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath
    {
        if (indexPath.row == 0)
        {
            return nil;
        }
    
        return indexPath;
    }
  10. 最后在didSelectRowAtIndexPath中,检查if(indexpath.row!= 0)并继续。

    - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
    {
        if (indexPath.row != 0)
        {
            int row = indexPath.row -1; //Now use 'row' in place of indexPath.row
    
            //Do what ever you want the selection to perform
        }
    }

这样就完成了。现在,您有了一个完美滚动的非浮动节标题。


1
实现了“ willSelectRowAtIndexPath”,以便控件在选择“节头”行时永远不会到达“ didSelectRowAtIndexPath”。
Sumit Anantwar 2013年

实际上,当选择标题时,我会折叠+展开行。但是很高兴知道您可以做到
Yariv Nissim 2013年

很好的答案,但是为什么'if(indexPath.row!= 0)'签入'didSelectRowAtIndexPath'?只是为了更加安全?使用“ willSelectRowAtIndexPath”实现,该行永远不能为0,对吧?
2013年

1
@ Glenn85,抱歉回复晚。是的,这只是为了增加安全性。
Sumit Anantwar

28

在您的Interface Builder中,单击问题表视图

问题表视图

然后导航到Attributes Inspector并将Style Plain更改为Grouped;)Easy

简单


17

关于UITableViewStyleGrouped的有趣的事情是tableView将样式添加到单元格而不是TableView。

样式作为名为UIGroupTableViewCellBackground的类作为backgroundView添加到单元格中,该类根据部分中单元格的位置处理绘制不同的背景。

因此,一个非常简单的解决方案是使用UITableViewStyleGrouped,将表的backgroundColor设置为clearColor,然后简单地替换cellForRow中单元格的backgroundView:

cell.backgroundView = [[[UIView alloc] initWithFrame:cell.bounds] autorelease];

1
这应该是最佳答案。有一个关于它的博客帖子在这里:corecocoa.wordpress.com/2011/09/17/...
萨姆

此解决方案实际上并不起作用。我一直在周围的单元格周围留有额外的空间。任何想法 ?
gsempe 2012年

2
单元格的contentView是否仍会向右移动?
Piotr Tomasik

15

更改您的TableView样式:

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

根据UITableView的Apple文档:

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

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


5

还有另一种棘手的方法。主要思想是将段号加倍,第一个仅显示headerView,第二个显示实际单元格。

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return sectionCount * 2;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    if (section%2 == 0) {
        return 0;
    }
    return _rowCount;
}

然后要做的是实现headerInSection委托:

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

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

这种方法对您的数据源也几乎没有影响:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    int real_section = (int)indexPath.section / 2;
    //your code
}

与其他方法相比,这种方法很安全,而无需更改tableview的frame或contentInsets。希望这会有所帮助。


5

获得所需内容的最简单方法是将表格样式设置为UITableViewStyleGrouped,分隔符样式设置为UITableViewCellSeparatorStyleNone

- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section {
    return CGFLOAT_MIN; // return 0.01f; would work same 
}

- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section {
    return [[UIView alloc] initWithFrame:CGRectZero];
}

nil在必须获得所需的内容后,请勿尝试将页脚视图返回为,也不要忘记设置页眉高度和页眉视图。


4

尽管这可能无法解决您的问题,但是当我想做类似的事情时,它为我做了。我没有设置页眉,而是使用了上面部分的页脚。使我难过的是,该部分实际上很小且是静态的,因此它从未滚动到视图底部下方。


好想法。我喜欢这个技巧。
史蒂夫·摩泽

有点好笑。欣赏这个主意,但骇客就是骇客。标头将开始在底部浮动,因此并不能真正解决问题
Shyam Bhat 2012年

4

对于Swift 3+

只需使用以下命令UITableViewStyleGrouped并将页脚的高度设置为零:

override func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
    return .leastNormalMagnitude
}

1
您可以返回0而不是.leastNormalMagnitude。您还需要实施tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView?
Ilias Karim

@IliasKarim将其设置为0会使其具有非零高度。同样,viewForFooterInSection在这种情况下也不必实现该功能。
陶Sengel

0.leastNormalMagnitude具有相同的效果。实现该viewForFooterSection方法是必要的。您应该返回nil。这是在带有Xcode 11.3.1的Swift 5.1上。
伊利亚斯·卡里姆

3

在思考如何解决此问题时,我记得有关UITableViewStyleGrouped的一个非常重要的细节。UITableView实现分组样式(单元格周围的圆形边框)的方式是通过向UITableViewCells(而不是UITableView)添加自定义backgroundView。根据每个单元格在节中的位置,为每个单元格添加一个backgroundView(上一行获得节边界的上部,中间行获得侧边界,而底部行获得–底部)。因此,如果我们只想要普通样式,而我们的单元格没有自定义backgroundView(在90%的情况下就是这种情况),那么我们要做的就是使用UITableViewStyleGrouped,并删除自定义背景。这可以通过执行以下两个步骤来完成:

将我们的tableView样式更改为UITableViewStyleGrouped,在返回单元格之前,将以下行添加到cellForRow中:

cell.backgroundView = [[[UIView分配] initWithFrame:cell.bounds]自动释放];

就是这样。除了浮动标题外,tableView样式将变得与UITableViewStylePlain完全相同。

希望这可以帮助!


2

也许您可以scrollViewDidScroll在tableView上使用并contentInset根据当前可见的标头更改。

看来适合我的用例!

extension ViewController : UIScrollViewDelegate{

    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        guard   let tableView = scrollView as? UITableView,
            let visible = tableView.indexPathsForVisibleRows,
            let first = visible.first else {
            return
        }

        let headerHeight = tableView.rectForHeader(inSection: first.section).size.height
        let offset =  max(min(0, -tableView.contentOffset.y), -headerHeight)
        self.tableView.contentInset = UIEdgeInsetsMake(offset, 0, -offset, 0)
   }
}

1

另一种方法是在想要标题的部分之前制作一个空白部分,并将标题放在该部分上。由于该部分为空,因此标题将立即滚动。


1

您可以在上面添加一个Section(零行),然后将上面的sectionFooterView设置为当前节的headerView,footerView不会浮动。希望能对您有所帮助。


FooterViews确实浮动,就像HeaderViews一样。它们只是漂浮在屏幕的底部,而不是顶部。
Mark A. Donohoe

0

忽略XAK。如果您希望自己的应用有机会被苹果接受,请不要探索任何私有方法。

如果您正在使用Interface Builder,这是最简单的。您可以在视图顶部添加UIView(图像将在其中放置),然后在其下方添加表格视图。IB应据此调整大小;也就是说,表格视图的顶部与您刚刚添加的UIView的底部接触,并且其高度覆盖了屏幕的其余部分。

这里的想法是,如果该UIView实际上不是表格视图的一部分,则它将不会随着表格视图滚动。即忽略tableview标头。

如果您不使用界面生成器,则它会稍微复杂一些,因为您必须正确设置表格视图的位置和高度。


这不能回答问题。Tricky指的是titleHeaderViews,它是位于单元格之间的视图,而不是您似乎建议的表头视图。
丹尼尔·伍德


0

要完全删除浮动节的标题节,可以执行以下操作:

- (UIView *) tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
    return [[UIView alloc] init];
}

return nil 不起作用。

要禁用浮动但仍显示节标题,您可以提供具有其自身行为的自定义视图。


0

@samvermette解决方案的一个变体:

/// Allows for disabling scrolling headers in plain-styled tableviews
extension UITableView {

    static let shouldScrollSectionHeadersDummyViewHeight = CGFloat(40)

    var shouldScrollSectionHeaders: Bool {
        set {
            if newValue {
                tableHeaderView = UIView(frame: CGRect(x: 0, y: 0, width: bounds.size.width, height: UITableView.shouldScrollSectionHeadersDummyViewHeight))
                contentInset = UIEdgeInsets(top: -UITableView.shouldScrollSectionHeadersDummyViewHeight, left: 0, bottom: 0, right: 0)
            } else {
                tableHeaderView = nil
                contentInset = .zero
            }
        }

        get {
            return tableHeaderView != nil && contentInset.top == UITableView.shouldScrollSectionHeadersDummyViewHeight
        }
    }

}

0

该代码段仅在第一部分显示粘性标题。其他节标题将与单元格一起浮动。

func tableView(_ tableView: UITableView, willDisplayHeaderView view: UIView, forSection section: Int) {

    if section == 1 {
        tableView.contentInset = .zero
    }

}

func tableView(_ tableView: UITableView, didEndDisplayingHeaderView view: UIView, forSection section: Int) {
    if section == 0 {
        tableView.contentInset = .init(top: -tableView.sectionHeaderHeight, left: 0, bottom: 0, right: 0)
    }
}

-1

这可以通过在UITableViewController的viewDidLoad方法中手动分配标题视图而不是使用委托的viewForHeaderInSection和heightForHeaderInSection来实现。例如,在UITableViewController的子类中,您可以执行以下操作:

- (void)viewDidLoad {
    [super viewDidLoad];

    UILabel *headerView = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 0, 40)];
    [headerView setBackgroundColor:[UIColor magentaColor]];
    [headerView setTextAlignment:NSTextAlignmentCenter];
    [headerView setText:@"Hello World"];
    [[self tableView] setTableHeaderView:headerView];
}

当用户滚动时,标题视图将消失。我不知道为什么这样做会如此,但似乎可以实现您想要的目标。


这不是针对某个部分,而是针对顶部的1个标头。
makdad

-1

也许您可以简单地使标题视图背景透明:

- (void)tableView:(UITableView *)tableView willDisplayHeaderView:(UIView *)view forSection:(NSInteger)section {
    view.tintColor = [UIColor clearColor];
}

或在全球范围内应用:

    [UITableViewHeaderFooterView appearance].tintColor = [UIColor clearColor];

-2

我还有另一个更简单的解决方案,可以不使用自动布局,并且可以通过XIB完成所有操作:

1 /通过将标题直接拖放到表格视图中,将其放在表格视图中。

2 /在新创建的页眉的“大小”检查器中,只需更改其自动大小设置即可:您只应保留顶部,左侧和右侧锚点以及水平填充。

那就是应该为标题设置的方式

这应该够了吧 !


-2

根据@samvermette的回答,我已经在Swift中实现了上述代码,以使编码人员可以轻松使用swift。

let dummyViewHeight = CGFloat(40)
self.tableView.tableHeaderView = UIView(frame: CGRect(x: 0, y: 0, width: self.tableView.bounds.size.width, height: dummyViewHeight))
self.tableView.contentInset = UIEdgeInsetsMake(-dummyViewHeight, 0, 0, 0)

-2

参考:https : //stackoverflow.com/a/26306212

let tableView = UITableView(frame: .zero, style: .grouped)

暂停

func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
    if section == 0 {
        return 40
    }else{
        tableView.sectionHeaderHeight = 0
    }

    return 0
}

这有助于使用标题空间


请不要这样做🙏–
MQoder

-3

2020最新更新

已通过Xcode 14。

要隐藏任何节标题,请返回nil作为节委托的标题

func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
    return nil
}

-4

您可以通过在tableview委托类中实现viewForHeaderInSection方法来轻松实现此目的。此方法期望将UIView作为返回对象(这是您的标头视图)。我在代码中做了同样的事情


原始海报不需要自定义标题。他们想要的标题不会在表格顶部“浮动”。您的建议仍然存在。
jemmons,2010年

使用viewforheader将tableview更改为UITableViewStyleGrouped,然后它将使其停止
mskw 2014年
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.