【OC-UITableView】iOS11 设置 heightForHeaderInSection 和 sectionFooterHeight 无效问题

首语

今天项目中一个需求,具体原型图如下图。
原始需求图

需要在设置了 UITableViewUITableViewStyleGrouped 样式后,头部需要设置一个温馨提示的标签。本来实现的方式有多种方式,比如:

  • ① 可以选择通过 tableHeaderView 属性创建一个 UIView,并在其上添加一个 UILabel 即可以实现。
 (nonatomic, strong, nullable) UIView *tableHeaderView;  
// accessory view for above row content. default is nil. not to be confused with section header
  • ② 可以选择通过下面方法直接返回 title 即可以实现。
- (nullable NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section;    
// fixed font style. use custom view (UILabel) if you want something different
  • ③ 可以通过下面方法直接自定义 viewForHeaderInSection ,并在其上添加一个 UILabel 即可以实现。
- (nullable UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section;   
// custom view for header. will be adjusted to default or specified header height

本以为最多几分钟的事儿,基本上就可以实现了。但是天公如此不作美,发现效果比较骨感,上一下效果图(这里是测试 Demo,只是简例)。

第一次实现效果图

备注:
环境:Xcode 9.4.1,iOS 11.4

初次实现效果问题

看看效果,嗯?第一个 sectionHeader 的高度和其他的一样?
有问题!
有问题!
绝对有问题!

此处上一下写得代码(测试 Demo):

//
//  ViewController.m
//  TestDemo11
//
//  Created by 苜蓿鬼仙 on 2018/7/23.
//  Copyright © 2018 苜蓿鬼仙. All rights reserved.
//

#import "ViewController.h"
 ViewController ()<UITableViewDataSource, UITableViewDelegate>
 (nonatomic, strong) UITableView *tableView;

 ViewController

- (void)viewDidLoad {
    [super viewDidLoad];


    self.tableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 64, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height-64) style:UITableViewStyleGrouped];
    self.tableView.backgroundColor = [UIColor lightGrayColor];
    self.tableView.dataSource      = self;
    self.tableView.delegate        = self;
    self.tableView.rowHeight       = 50;
    self.tableView.showsVerticalScrollIndicator = NO;
    [self.view addSubview:self.tableView];

    [self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"cell"];
}

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return 5;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return 1;
}

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
    if (section == 0) {
        return @"温馨提示:左滑列表可以编辑广告";
    }
    return nil;
}

- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
    if (section == 0) {
        return 40;
    } else {
        return 20;
    }
}

- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section {
    return CGFLOAT_MIN;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell" forIndexPath:indexPath];

    cell.textLabel.text = [indexPath description];
    cell.selectionStyle = UITableViewCellSelectionStyleNone;

    return cell;
}

想想以前也是如此写得呀,貌似没有啥子问题呀,经过调试发现:

- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
    if (section == 0) {
        return 40;
    } else {
        return 20;
    }
}

这个方法只调用了一次。

这就肯定不对了,如果只会调用一次的话,则肯定所有的高度都会和第一个 sectionHeader 的高度相同了。后面又再次调试了一段时间,发现没有灵感,然后向群里的一些大神们求助,发现相同的代码及环境,发现大神们的并没有任何问题,唯一的区别就是大神们是通过 StoryBoard 创建的,我这边则是通过纯代码创建的。

然后我也尝试通过 StoryBoard 创建了一下,发现问题解决了!!!

这是闹哪样???逗我呢???肯定有猫腻!!!

最后经过大神的提醒,发现了一些猫腻,是关于 自动计算(UITableViewAutomaticDimension) 上的问题。

系统 UITableView 的 API

 (nonatomic) CGFloat rowHeight;             // default is UITableViewAutomaticDimension (nonatomic) CGFloat sectionHeaderHeight;   // default is UITableViewAutomaticDimension (nonatomic) CGFloat sectionFooterHeight;   // default is UITableViewAutomaticDimension (nonatomic) CGFloat estimatedRowHeight NS_AVAILABLE_IOS(7_0); // default is UITableViewAutomaticDimension, set to 0 to disable (nonatomic) CGFloat estimatedSectionHeaderHeight NS_AVAILABLE_IOS(7_0); // default is UITableViewAutomaticDimension, set to 0 to disable (nonatomic) CGFloat estimatedSectionFooterHeight NS_AVAILABLE_IOS(7_0); // default is UITableViewAutomaticDimension, set to 0 to disable
// Variable height support

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath;
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section;
- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section;

// Use the estimatedHeight methods to quickly calcuate guessed values which will allow for fast load times of the table.
// If these methods are implemented, the above -tableView:heightForXXX calls will be deferred until views are ready to be displayed, so more expensive logic can be placed there.
- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath NS_AVAILABLE_IOS(7_0);
- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForHeaderInSection:(NSInteger)section NS_AVAILABLE_IOS(7_0);
- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForFooterInSection:(NSInteger)section NS_AVAILABLE_IOS(7_0);

// Section header & footer information. Views are preferred over title should you decide to provide both

- (nullable UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section;   // custom view for header. will be adjusted to default or specified header height
- (nullable UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section;   // custom view for footer. will be adjusted to default or specified footer height

从此处可以看到这个系统的 API,默认都是 UITableViewAutomaticDimension 自动计算的模式。

然后我们再看看 StoryBoard 中这些参数是如何的:
iOS11 StoryBoard 中 UITableView 高度相关参数

从此处可以看出 StoryBoard 中,除了 Row Height 是默认 Automatic 自动计算的,Header HeightFooter Height 都不是默认 Automatic 动计算的。

但是依据上面的系统 API 可以看出,如果是纯代码写得,应该是默认全部都是 Automatic 自动计算的。

看来问题应该就是出在这里了,赶紧改代码试试。

解决方案

 (nonatomic) CGFloat estimatedSectionHeaderHeight NS_AVAILABLE_IOS(7_0); // default is UITableViewAutomaticDimension, set to 0 to disable (nonatomic) CGFloat estimatedSectionFooterHeight NS_AVAILABLE_IOS(7_0); // default is UITableViewAutomaticDimension, set to 0 to disable


self.tableView.estimatedSectionHeaderHeight = 0;
self.tableView.estimatedSectionFooterHeight = 0;

estimatedSectionHeaderHeightestimatedSectionFooterHeight 都设置为 0,即可以取消 Automatic 自动计算了。

最终代码及效果图

//
//  ViewController.m
//  TestDemo11
//
//  Created by 苜蓿鬼仙 on 2018/7/23.
//  Copyright © 2018 苜蓿鬼仙. All rights reserved.
//

#import "ViewController.h"
 ViewController ()<UITableViewDataSource, UITableViewDelegate>
 (nonatomic, strong) UITableView *tableView;

 ViewController

- (void)viewDidLoad {
    [super viewDidLoad];


    self.tableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 64, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height-64) style:UITableViewStyleGrouped];
    self.tableView.backgroundColor = [UIColor lightGrayColor];
    self.tableView.dataSource      = self;
    self.tableView.delegate        = self;
    self.tableView.rowHeight       = 50;
    self.tableView.estimatedSectionHeaderHeight = 0;
    self.tableView.estimatedSectionFooterHeight = 0;
    self.tableView.showsVerticalScrollIndicator = NO;
    [self.view addSubview:self.tableView];

    [self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"cell"];
}

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return 5;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return 1;
}

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
    if (section == 0) {
        return @"温馨提示:左滑列表可以编辑广告";
    }
    return nil;
}

- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
    if (section == 0) {
        return 40;
    } else {
        return 20;
    }
}

- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section {
    return CGFLOAT_MIN;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell" forIndexPath:indexPath];

    cell.textLabel.text = [indexPath description];
    cell.selectionStyle = UITableViewCellSelectionStyleNone;

    return cell;
}

最终效果图

<br>
<p>

© 著作权归作者所有
这个作品真棒,我要支持一下!
该专栏主要用于记录日常iOS开发中的笔记心得,包括 Objective-C、Swift、Mac OS 开发及学习随笔。
0条评论
top Created with Sketch.