如何使用Storyboard实现自定义表视图节页眉和页脚

时间:2012-02-09 21:41:36

标签: uitableview ios5 uistoryboard

如果不使用故事板,我们只需将UIView拖到画布上,然后将其布局,然后在tableView:viewForHeaderInSectiontableView:viewForFooterInSection委托方法中进行设置。

我们如何使用StoryBoard完成此操作,我们无法将UIView拖到画布上

16 个答案:

答案 0 :(得分:380)

只需使用原型单元格作为节标题和/或页脚。

  • 添加一个额外的单元格并将所需的元素放入其中。
  • 将标识符设置为特定的(在我的情况下为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:方法中,请致电:
  4. 获取标签
        UILabel *label = (UILabel *)[headerView viewWithTag:123]; 
    
    1. 现在您可以使用标签设置新标题:
    2.     [label setText:@"New Title"];
      

答案 1 :(得分:88)

我知道这个问题适用于iOS 5,但为了未来读者的利益,请注意有效的iOS 6我们现在可以使用dequeueReusableHeaderFooterViewWithIdentifier代替dequeueReusableCellWithIdentifier

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

答案 2 :(得分:52)

在iOS 6.0及更高版本中,新的dequeueReusableHeaderFooterViewWithIdentifier API已经发生了变化。

我写了guide(在iOS 9上测试过),可以这样总结:

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

答案 3 :(得分:22)

我在故事板中使用原型单元在iOS7中工作。我的自定义部分标题视图中有一个按钮,用于触发在故事板中设置的segue。

Tieme's solution

开始

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

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

然而,正如Hons指出的那样,长按此段标题会使应用程序崩溃。

解决方案是从contentView中删除任何gestureRecognizers。

-(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; }

如果您在部分标题视图中没有使用手势,那么这个小黑客似乎就可以完成它。

答案 4 :(得分:13)

如果您使用故事板,则可以使用tableview中的原型单元格来布局标题视图。设置一个唯一的id和viewForHeaderInSection,您可以使用该ID将单元格出列并将其转换为UIView。

答案 5 :(得分:11)

如果你需要一个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
}

答案 6 :(得分:9)

我提出的解决方案基本上与故事板引入之前使用的解决方案相同。

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

手动加载笔尖,在viewForHeaderInSection或viewForFooterInSection委托方法中指定相应的页眉/页脚部分。

我希望Apple能够通过故事板简化这种情况,并不断寻找更好或更简单的解决方案。例如,可以直接添加自定义表格页眉和页脚。

答案 7 :(得分:5)

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

  1. 与手势相关的崩溃
  2. 您没有重复使用contentView(每次viewForHeaderInSection调用时,您都在创建新单元格)
  3. 解决方案:

    表头\页脚的包装类。 它只是容器,继承自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;
    }
    

答案 8 :(得分:2)

我过去常常懒惰地创建页眉/页脚视图:

  • 为部分页眉/页脚添加自由形式视图控制器到故事板
  • 处理视图控制器中标题的所有内容
  • 在表格视图中,控制器为使用[NSNull null]
  • 重新填充的部分页眉/页脚提供可变数组的视图控制器
  • 在viewForHeaderInSection / viewForFooterInSection中,如果视图控制器尚不存在,请使用storyboards instantiateViewControllerWithIdentifier创建它,记住它在数组中并返回视图控制器视图

答案 9 :(得分:2)

我甚至在没有重复使用Header的情况下遇到了麻烦。

因此,对于想要实现show empty sections(0行)情况的每个人的提示,请注意:

dequeueReusableHeaderFooterViewWithIdentifier在您返回至少一行之前不会重复使用标题

希望有所帮助

答案 10 :(得分:1)

要跟进Damon's suggestion,以下是我如何选择标题,就像带有公开指标的普通行一样。

我在UIButton(子类名称" ButtonWithArgument")中添加了一个Button子标题到标题的原型单元格并删除了标题文本(粗体"标题"文本是另一个UILabel在原型单元格中)

Button In Interface Builder

然后将Button设置为整个标题视图,并添加了一个带有Avario's trick

的披露指示符
- (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

答案 11 :(得分:1)

您应该使用Tieme's solution作为基础但忘记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;
}  

答案 12 :(得分: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])
    }
}

答案 13 :(得分:0)

这是@Vitaliy Gozhenko's answer,在Swift中 总而言之,您将创建一个包含UITableViewCell的UITableViewHeaderFooterView。这个UITableViewCell将是“dequeuable”,您可以在故事板中进行设计。

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

答案 14 :(得分:0)

  1. StoryBoard中添加单元格,然后设置reuseidentified

    sb

  2. 代码

    class TP_TaskViewTableViewSectionHeader: UITableViewCell{
    }
    

    link

  3. 使用:

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

答案 15 :(得分:0)

与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
}