如何使用情节提要板实现自定义表格视图节的页眉和页脚


176

无需使用情节提要,我们可以简单地将a UIView拖到画布上,进行布局,然后在tableView:viewForHeaderInSectiontableView:viewForFooterInSection委托方法中进行设置。

我们如何通过StoryBoard做到这一点,而不能将UIView拖到画布上

Answers:


90

我知道这个问题是针对iOS 5的,但是为了将来的读者受益,请注意,我们现在可以使用有效的iOS 6 dequeueReusableHeaderFooterViewWithIdentifier代替dequeueReusableCellWithIdentifier

因此viewDidLoad,请致电registerNib:forHeaderFooterViewReuseIdentifier:registerClass:forHeaderFooterViewReuseIdentifier:。然后在viewForHeaderInSection,请致电tableView:dequeueReusableHeaderFooterViewWithIdentifier:。您不能使用带有该API的单元原型(它是基于NIB的视图或以编程方式创建的视图),但这是用于出队的页眉和页脚的新API。


13
可惜,有关使用NIB代替原细胞这两个干净的答案,目前正在5票,而“与原细胞本事”的答案是高达200以上
Benjohn

5
区别在于,借助来自Tieme的黑客攻击,您可以在同一情节提要中进行设计,而无需使用单独的
Xib

您能以某种方式避免使用单独的NIB文件,而是使用情节提要吗?
Foriger

@Foriger-暂时没有。在情节提要表视图中这是一个奇怪的遗漏,但这是事实。如果需要,可以将它与原型单元一起使用,但我个人只是忍受了NIB对于页眉/页脚视图的烦恼。
罗布(Rob)

2
恕我直言,仅说它“旧”还不够坚固。公认的答案是一种避免事实的方法,即您不能拥有用于页眉和页脚视图的原型单元。但我认为这是完全错误的,因为它假定页眉和页脚的出队与单元的出队相同(对于页眉/页脚,较新的API的存在可能并非如此)。可接受的答案是一种创造性的解决方法,在iOS 5中是可以理解的,但是在iOS 6及更高版本中,最好使用显式设计的API,以便重新使用页眉/页脚。
罗布(Rob)

384

只需使用原型单元格作为您的节的页眉和/或页脚。

  • 添加一个额外的单元格,然后将所需的元素放入其中。
  • 将标识符设置为特定的内容(在我的情况下为SectionHeader)
  • 实施 tableView:viewForHeaderInSection:方法或tableView:viewForFooterInSection:方法
  • [tableView dequeueReusableCellWithIdentifier:]获取标题
  • 实现该tableView:heightForHeaderInSection:方法。

(see screenhot)

-(UIView *) tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
    static NSString *CellIdentifier = @"SectionHeader"; 
    UITableViewCell *headerView = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (headerView == nil){
        [NSException raise:@"headerView == nil.." format:@"No cells with matching CellIdentifier loaded from your storyboard"];
    }
    return headerView;
}  

编辑:如何更改标题标题(注释的问题):

  1. 向标题单元格添加标签
  2. 将标签的标签设置为特定数字(例如123)
  3. 在您的tableView:viewForHeaderInSection:方法中,通过调用以下命令获取标签:
    UILabel *label = (UILabel *)[headerView viewWithTag:123]; 
  1. 现在,您可以使用标签设置新标题:
    [label setText:@"New Title"];

7
聚会晚了,但是要禁用对节标题视图的单击,只需在viewForHeaderInSection 方法中返回cell.contentView (无需添加任何自定义UIView)。
paulvs 2014年

3
@PaulVon我尝试在我的最新项目中执行此操作,但是如果执行此操作,则尝试长按其中一个标头,它将崩溃
Hons

13
在iOS 6.0中dequeueReusableHeaderFooterViewWithIdentifier被引入,现在比该答案更可取。但是现在正确使用它需要更多步骤。我有指南@ samwize.com / 2015/11 / 06/…
samwize

3
不要使用UITableViewCells创建sectionHeaderViews,它会创建意外的行为。请改用UIView。
AppreciateIt'6

4
请永远不要UITableViewCell用作标题视图。您将很难调试视觉故障-标题有时会由于单元出队的方式而消失,并且您要花几个小时才能找到原因,直到您意识到自己UITableViewCell不属于UITableView标题。
raven_raven

53

在iOS 6.0及更高版本中,新版本已改变了一切 dequeueReusableHeaderFooterViewWithIdentifier API。

我写了一个指南(在iOS 9上测试过),可以总结如下:

  1. 子类 UITableViewHeaderFooterView
  2. 使用子类视图创建Nib,并添加1个容器视图,其中在页眉/页脚中包含所有其他视图
  3. 注册笔尖 viewDidLoad
  4. 实施viewForHeaderInSection并用于dequeueReusableHeaderFooterViewWithIdentifier获取页眉/页脚

2
感谢您提供的答案,这不是hack,也是做事的正确方法。另外,感谢您向我介绍UITableViewHeaderFooterVIew。顺便说一句,指南是非常好的!:)
Roboris

欢迎。Apple并没有很好地记录为表或集合视图实现页眉/页脚。就在昨天,当我通过Storyboad进行设计时,我陷入了getter标头中无法显示。
samwize

1
当然,这应该是评分最高的答案!
本·威廉姆斯

您能否通过情节提要而不是xib解释如何做到这一点?这样做时,我成功出队,但我的UILabels为空。
bunkerdive

22

我使用情节提要中的原型单元在iOS7中使用它。我的自定义部分标题视图中有一个按钮,该按钮触发在情节提要中设置的segue。

Tieme的解决方案开始

正如pedro.m所指出的那样,这样做的问题是,点击节标题会导致选择节中的第一个单元格。

正如Paul Von所指出的,此问题通过返回单元格的contentView而不是整个单元格来解决。

但是,正如Hons所指出的那样,长按该部分标题将使应用程序崩溃。

解决方案是从contentView中删除所有gestureRecognizer。

-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
     static NSString *CellIdentifier = @"SectionHeader";
     UITableViewCell *sectionHeaderView = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

     while (sectionHeaderView.contentView.gestureRecognizers.count) {
         [sectionHeaderView.contentView removeGestureRecognizer:[sectionHeaderView.contentView.gestureRecognizers objectAtIndex:0]];
     }

     return sectionHeaderView.contentView; }

如果您在节标题视图中没有使用手势,那么这个小技巧似乎可以解决问题。


2
你是对的。我刚刚测试了它及其工作原理,所以更新了我的帖子(hons82.blogspot.it/2014/05/uitableviewheader-done-right.html),其中包含我在研究过程中发现的所有可能解决方案。此修复非常感谢
Hons 2014年

好的答案...谢谢男人
Jogendra.Com

13

如果使用情节提要,则可以在表视图中使用原型单元格来布局标题视图。设置一个唯一的id和viewForHeaderInSection,您可以使用该ID使单元出队,并将其强制转换为UIView。


12

如果您需要对此的Swift实现,请按照接受的答案上的说明进行操作,然后在UITableViewController中实现以下方法:

override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
    return tableView.dequeueReusableCell(withIdentifier: "CustomHeader")
}

override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
    return 75
}

2
由于某种原因,它对我不起作用,直到我也重写了heightForHeaderInSection。
Entalpi

这是很旧的代码。您应该发布另一个答案!
JZ。

我被卡住是因为我不知道我必须重写heightForHeaderInSection。感谢@Entalpi
guijob

1
@JZ。在您的Swift 3示例中,您使用以下self.tableView.dequeueReusableHeaderFooterView和Swift 2出队:self.tableView.dequeueReusableCellWithIdentifier有区别吗?
Vrutin Rathod

1
这救了我的屁股!向headerCell添加高度使其可见。
CRey

9

我想出的解决方案基本上与引入情节提要之前使用的解决方案相同。

创建一个新的空接口类文件。将UIView拖到画布上,根据需要进行布局。

手动加载笔尖,将其分配给viewForHeaderInSection或viewForFooterInSection委托方法中的相应页眉/页脚部分。

我曾希望Apple通过情节提要简化此方案,并一直在寻找更好或更简单的解决方案。例如,自定义表的页眉和页脚可以直接添加。


如果您使用情节
提要

2
除了仅适用于原型单元,不适用于静态单元。
Jean-Denis Muys

1
一种真正简单的解决方案是使用Pixate。您可以进行很多自定义,而无需引用标题。您所要做的就是tableView:titleForHeaderInSection,这是一个单行代码。
John Starr Dewar

5
那是真正的解决方案……不幸的是,它并不是最重要的,所以我花了一段时间才找到它。我总结了我遇到的问题hons82.blogspot.it/2014/05/uitableviewheader-done-right.html
荣誉

比这简单得多,只需拖放即可。
里卡多

5

当您返回单元格的contentView时,您将遇到两个问题:

  1. 与手势有关的崩溃
  2. 您不会重复使用contentView(每次viewForHeaderInSection通话时,您都会创建新的单元格)

解:

表页眉\页脚的包装器类。它只是继承自的容器,UITableViewHeaderFooterView将单元格保存在其中

https://github.com/Magnat12/MGTableViewHeaderWrapperView.git

在您的UITableView中注册类(例如,在viewDidLoad中)

- (void)viewDidLoad {
    [super viewDidLoad];
    [self.tableView registerClass:[MGTableViewHeaderWrapperView class] forHeaderFooterViewReuseIdentifier:@"ProfileEditSectionHeader"];
}

在您的UITableViewDelegate中:

- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
    MGTableViewHeaderWrapperView *view = [tableView dequeueReusableHeaderFooterViewWithIdentifier:@"ProfileEditSectionHeader"];

    // init your custom cell
    ProfileEditSectionTitleTableCell *cell = (ProfileEditSectionTitleTableCell * ) view.cell;
    if (!cell) {
        cell = [tableView dequeueReusableCellWithIdentifier:@"ProfileEditSectionTitleTableCell"];
        view.cell = cell;
    }

    // Do something with your cell

    return view;
}

2

我过去经常做以下事情来懒惰地创建页眉/页脚视图:

  • 将节的页眉/页脚添加到情节提要的自由格式视图控制器
  • 在视图控制器中处理标题的所有内容
  • 在表格中,视图控制器为重新填充了部分标题/页脚的视图控制器提供了可变数组 [NSNull null]
  • 在viewForHeaderInSection / viewForFooterInSection中,如果视图控制器尚不存在,则使用情节提要板创建它,实例化ViewControllerWithIdentifier,将其记住在数组中,然后返回视图控制器视图

2

我遇到了这样一种情况,即即使执行所有正确的步骤也从未重用Header。

因此,给所有想要实现显示空白部分(0行)的情况的提示要警告:

dequeueReusableHeaderFooterViewWithIdentifier将不会重复使用标头,直到您返回至少一行

希望能帮助到你


1

为了跟进Damon的建议,以下是我如何使标题可选,就像带有显示指示器的普通行一样。

我将UIButton子类的Button(子类名“ ButtonWithArgument”)添加到标题的原型单元中,并删除了标题文本(粗体的“ Title”文本是原型单元中的另一个UILabel)

接口生成器中的按钮

然后将“按钮”设置为整个标题视图,并使用Avario的技巧添加一个公开指示器

- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
    static NSString *CellIdentifier = @"PersonGroupHeader";
    UITableViewCell *headerView = (UITableViewCell *) [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if(headerView == nil)
    {
        [NSException raise:@"headerView == nil, PersonGroupTableViewController" format:[NSString stringWithFormat:@"Storyboard does not have prototype cell with identifier %@",CellIdentifier]];
    }

    //  https://stackoverflow.com/a/24044628/3075839
    while(headerView.contentView.gestureRecognizers.count)
    {
        [headerView.contentView removeGestureRecognizer:[headerView.contentView.gestureRecognizers objectAtIndex:0]];
    }


    ButtonWithArgument *button = (ButtonWithArgument *)[headerView viewWithTag:4];
    button.frame = headerView.bounds; // set tap area to entire header view
    button.argument = [[NSNumber alloc] initWithInteger:section]; // from ButtonWithArguments subclass
    [button addTarget:self action:@selector(headerViewTap:) forControlEvents:UIControlEventTouchUpInside];

    // https://stackoverflow.com/a/20821178/3075839
    UITableViewCell *disclosure = [[UITableViewCell alloc] init];
    disclosure.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
    disclosure.userInteractionEnabled = NO;
    disclosure.frame = CGRectMake(button.bounds.origin.x + button.bounds.size.width - 20 - 5, // disclosure 20 px wide, right margin 5 px
          (button.bounds.size.height - 20) / 2,
          20,
          20);
    [button addSubview:disclosure];

    // configure header title text

    return headerView.contentView;
}

- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
    return 35.0f;
}

-(void) headerViewTap:(UIGestureRecognizer *)gestureRecognizer;
{
    NSLog(@"header tap");
    NSInteger section = ((NSNumber *)sender.argument).integerValue;
    // do something here
}

ButtonWithArgument.h

#import <UIKit/UIKit.h>

@interface ButtonWithArgument : UIButton
@property (nonatomic, strong) NSObject *argument;
@end

ButtonWithArgument.m

#import "ButtonWithArgument.h"
@implementation ButtonWithArgument
@end

1

您应该使用Tieme的解决方案作为基础,但是忘了viewWithTag:和其他可疑的方法,而是尝试重新加载标头(通过重新加载该部分)。

因此,在您将自定义单元标题视图中包含所有奇特的AutoLayout东西之后,只需将其出队并在设置后返回contentView即可,例如:

-(UIView *) tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
 static NSString *CellIdentifier = @"SectionHeader"; 

    SettingsTableViewCell *sectionHeaderCell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

    sectionHeaderCell.myPrettyLabel.text = @"Greetings";
    sectionHeaderCell.contentView.backgroundColor = [UIColor whiteColor]; // don't leave this transparent

    return sectionHeaderCell.contentView;
}  

1

那么标题基于视图数组的解决方案呢?

class myViewController: UIViewController {
    var header: [UILabel] = myStringArray.map { (thisTitle: String) -> UILabel in
        let headerView = UILabel()
            headerView.text = thisTitle
    return(headerView)
}

代表中的下一个:

extension myViewController: UITableViewDelegate {
    func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
        return(header[section])
    }
}

1
  1. 在中添加单元格StoryBoard并设置reuseidentified

    某人

  2. class TP_TaskViewTableViewSectionHeader: UITableViewCell{
    }
    

    链接

  3. 用:

    func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
        let header = tableView.dequeueReusableCell(withIdentifier: "header", for: IndexPath.init(row: 0, section: section))
        return header
    }
    

2
这不是添加标题的正确方法
Manish Malviya

2
那怎么回事
Pramod Shukla

1

与laszlo答案类似,但是您可以将相同的原型单元重用于表单元和节头单元。将下面的前两个函数添加到您的UIViewController子类中

override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let cell = tableView.dequeueReusableCell(withIdentifier: "DataCell") as! DataCell
    cell.data1Label.text = "DATA KEY"
    cell.data2Label.text = "DATA VALUE"
    return cell
}

override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
    return 75
}

// Example of regular data cell dataDelegate to round out the example
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "DataCell", for: indexPath) as! PlayerCell

    cell.data1Label.text = "\(dataList[indexPath.row].key)"
    cell.data2Label.text = "\(dataList[indexPath.row].value)"
    return cell
}

0

这是@Vitaliy Gozhenko在Swift中的回答
总而言之,您将创建一个包含UITableViewCell的UITableViewHeaderFooterView。此UITableViewCell将是“可取消队列的”,您可以在情节提要中对其进行设计。

  1. 创建一个UITableViewHeaderFooterView类

    class CustomHeaderFooterView: UITableViewHeaderFooterView {
    var cell : UITableViewCell? {
        willSet {
            cell?.removeFromSuperview()
        }
        didSet {
            if let cell = cell {
                cell.frame = self.bounds
                cell.autoresizingMask = [UIViewAutoresizing.FlexibleHeight, UIViewAutoresizing.FlexibleWidth]
                self.contentView.backgroundColor = UIColor .clearColor()
                self.contentView .addSubview(cell)
            }
        }
    }
    
  2. 使用viewDidLoad函数中的此类将tableview插入:

    self.tableView.registerClass(CustomHeaderFooterView.self, forHeaderFooterViewReuseIdentifier: "SECTION_ID")
    
  3. 询问部分标题时,将CustomHeaderFooterView出队,并在其中插入一个单元格

    func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
        let view = self.tableView.dequeueReusableHeaderFooterViewWithIdentifier("SECTION_ID") as! CustomHeaderFooterView
        if view.cell == nil {
            let cell = self.tableView.dequeueReusableCellWithIdentifier("Cell")
            view.cell = cell;
        }
    
        // Fill the cell with data here
    
        return view;
    }
    
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.