如何使用Storyboard实现自定义表格视图部分页眉和页脚
不使用故事板,我们可以简单地将UIView
拖到canvas上,然后将其放置在tableView:viewForHeaderInSection
或tableView:viewForFooterInSection
委托方法中。
我们如何用StoryBoard来完成这个工作,我们不能将UIView拖到canvas上
只需使用原型单元格作为您的部分页眉和/或页脚。
- 添加一个额外的单元格,并把你想要的元素在里面。
- 设置标识符为特定的东西(在我的情况下,SectionHeader)
- 实现
tableView:viewForHeaderInSection:
方法或tableView:viewForFooterInSection:
方法 - 使用
[tableView dequeueReusableCellWithIdentifier:]
来获取标题 - 实现
tableView:heightForHeaderInSection:
方法。
-(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; }
编辑:如何更改标题标题(注释的问题):
- 向标题单元格添加一个标签
- 将标签的标签设置为特定的数字(例如123)
- 在你的
tableView:viewForHeaderInSection:
方法通过调用获取标签:
UILabel *label = (UILabel *)[headerView viewWithTag:123];
- 现在你可以使用标签来设置一个新的标题:
[label setText:@"New Title"];
我知道这个问题是针对iOS 5的,但为了未来读者的利益,请注意,我们现在可以使用dequeueReusableHeaderFooterViewWithIdentifier
而不是dequeueReusableCellWithIdentifier
。
因此,在viewDidLoad
,调用registerNib:forHeaderFooterViewReuseIdentifier:
或registerClass:forHeaderFooterViewReuseIdentifier:
然后在viewForHeaderInSection
,调用tableView:dequeueReusableHeaderFooterViewWithIdentifier:
viewForHeaderInSection
您不使用此API的单元格原型(它是基于NIB的视图或以编程方式创build的视图),但是这是用于出列页眉和页脚的新API。
在iOS 6.0和更高版本中,新的dequeueReusableHeaderFooterViewWithIdentifier
API已经改变了。
我写了一个指南 (在iOS 9上testing),可以概括为:
- 子类
UITableViewHeaderFooterView
- 使用子类视图创buildNib,并在页眉/页脚中添加1个包含所有其他视图的容器视图
- 在
viewDidLoad
注册Nib - 实现
viewForHeaderInSection
并使用dequeueReusableHeaderFooterViewWithIdentifier
获取页眉/页脚
我使用故事板中的原型单元在iOS7中工作。 我在自定义区域标题视图中有一个button,触发在故事板中设置的segue。
从铁梅的解决scheme开始
正如pedro.m所指出的那样,这个问题在于敲击节标题会导致节中的第一个单元格被选中。
正如Paul Von指出的那样,通过返回单元格的contentView而不是整个单元格来解决这个问题。
然而,正如Hons所指出的那样,长时间按下该部分标题将会使应用程序崩溃。
解决scheme是从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; }
如果你在部分头部视图中没有使用手势,这个小小的破解似乎就完成了。
如果使用故事板,则可以使用tableview中的原型单元格来布局标题视图。 设置一个唯一的ID和viewForHeaderInSection你可以出队的单元格与该ID并将其投射到一个UIView。
我提出的解决scheme基本上是在介绍故事板之前使用的解决scheme。
创build一个新的空接口类文件。 将UIView拖到canvas上,根据需要布局。
手动加载笔尖,分配给viewForHeaderInSection或viewForFooterInSection委托方法中适当的页眉/页脚部分。
我希望苹果公司用故事板简化了这种情况,并不断寻找更好或更简单的解决scheme。 例如自定义表头和页脚是直接添加。
如果你需要一个Swift实现这个按照接受的答案的指示,然后在你的UITableViewController实现以下方法:
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { let cell = self.tableView.dequeueReusableCell(withIdentifier: "CustomHeader") as! CustomHeaderUITableViewCell return cell } override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { return 75 }
当你返回单元格的contentView时,你会遇到2个问题:
- 与手势相关的崩溃
- 你不重复使用contentView(每次在
viewForHeaderInSection
调用,你创build新的单元格)
解:
表头\页脚的包装类。 它只是容器,从UITableViewHeaderFooterView
inheritance,里面持有单元格
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; }
我曾经做了以下事情懒洋洋地创build页眉/页脚视图:
- 为节标题/页脚添加一个自由视图控制器到故事板
- 在视图控制器中处理所有的东西
- 在表视图控制器提供了一个可变的数组控制器的节/页眉join
[NSNull null]
- 在viewForHeaderInSection / viewForFooterInSection中,如果视图控制器还不存在,用storyboards创build它instantiateViewControllerWithIdentifier,记住它在数组中并返回视图控制器视图
你应该使用Tieme的解决scheme作为基础,但忘记了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; }
跟进达蒙的build议 ,这里是我如何使标题可以select像一个正常的行与披露指标。
我从UIButton(子类名称“ButtonWithArgument”)中添加了一个Button子类,并将其删除了标题文本(粗体“Title”文本是原型单元格中的另一个UILabel)
然后将Button设置为整个标题视图,并添加了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
这里是@Vitaliy Gozhenko的答案 ,在Swift中。
总结一下,你将创build一个包含UITableViewCell的UITableViewHeaderFooterView。 这个UITableViewCell将是“美丽的”,您可以在故事板中进行devise。
-
创build一个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) } } }
-
在你的viewDidLoad函数中将你的tableview与这个类相关联:
self.tableView.registerClass(CustomHeaderFooterView.self, forHeaderFooterViewReuseIdentifier: "SECTION_ID")
-
当询问段落标题时,将一个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; }
我在一个情况下遇到了麻烦,在这个情况下,即使采取了所有适当的步骤,标题也从未被重用过。
所以作为一个提示,大家想要达到显示空白部分(0行)的情况应该警告:
dequeueReusableHeaderFooterViewWithIdentifier将不会重用该标题,直到您至less返回一行
希望它有帮助
怎么样解决scheme的标题是基于视图数组:
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]) } }
-
在
StoryBoard
添加单元格,并设置reuseidentified
-
码
class TP_TaskViewTableViewSectionHeader: UITableViewCell{ }
和
-
使用:
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { let header = tableView.dequeueReusableCell(withIdentifier: "header", for: IndexPath.init(row: 0, section: section)) return header }