由UITableView分割线说开去

来源:互联网 时间:2017-01-21


昨天遇到个老生常谈的问题,UITableView分割线。我们知道,iPhone上默认的分割线左边留有15个单位长度,想要去掉这个长度,网上杂七杂八很多,不管你三七二十一,一通无脑设置,就不说了,主要分析的是问题原因。
说下历史,iOS7之后,加入了separatorInset属性,separatorInset虽然是UIEdgeInsets类型,但是只有左右生效,也就是说,separatorInset定义了分割线到左边和右边的距离。自从有了这个家伙,分割线就不会延伸到table view的边界。显然,iOS7的时代,只要设置了separatorInset为UIEdgeInsetsZero,那么皆大欢喜。这里可能有人注意到,UITableView和UITableViewCell都有这个属性。区别在于 :


UITableView设置的是所有cell的separatorInset,这有点类似rowHeight。我们可以通过table view设置全局cell的inset,也可以单独为某个cell指定inset,后者的优先级大于前者。separatorInset默认为UIEdgeInsetsMake(0.0, 15.0, 0.0, 0.0)
除了全局设置,UITableView额外设置table view底部(无cell部分)的分割线

分别设置,看下效果


self.tableView.separatorColor = [UIColor orangeColor];
self.tableView.separatorInset = UIEdgeInsetsMake(0.0, 50.0, 0.0, 0.0);

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 10;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
NSLog(@"%@",NSStringFromUIEdgeInsets(cell.separatorInset));
cell.separatorInset = UIEdgeInsetsMake(0.0, 15.0 + 5 * indexPath.row, 0.0, 0.0);
cell.textLabel.text = [NSString stringWithFormat:@"row%ld",indexPath.row];
return cell;
}


4612396A-85BF-491A-8C6D-E25D55DA3021.jpeg

首先,table view设置全局cell的inset为UIEdgeInsetsMake(0.0, 50.0, 0.0, 0.0)。其次,根据所在row单独指定inset。


现在我们清楚separatorInset干嘛之后,现在看下layoutMargins。iOS8之后,加入了layoutMargins属性,layoutMargins定义了视图边界与子视图边界之间间距,非控制器根视图默认为UIEdgeInsetsMake(8.0, 8.0, 8.0, 8.0),即上下左右各8个单位长度。控制器根视图默认为UIEdgeInsetsMake(0.0, 16.0, 0.0, 16.0)或者UIEdgeInsetsMake(0.0, 20.0, 0.0, 20.0),取决于当前size class。我们可以改变非根视图的layoutMargins,但是不可以改变根视图的layoutMargins,它由系统设置管理。我们在根视图上添加一个灰色view,看下具体在自动布局中表现


代码


[NSLayoutConstraint constraintWithItem:lightGrayView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeTopMargin multiplier:1.0 constant:0.0].active = YES;
[NSLayoutConstraint constraintWithItem:lightGrayView attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeLeftMargin multiplier:1.0 constant:0.0].active = YES;
[NSLayoutConstraint constraintWithItem:lightGrayView attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeBottomMargin multiplier:1.0 constant:0.0].active = YES;
[NSLayoutConstraint constraintWithItem:lightGrayView attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeRightMargin multiplier:1.0 constant:0.0].active = YES;

VFL


[NSLayoutConstraint activateConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[lightGrayView]-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(lightGrayView)]];
[NSLayoutConstraint activateConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-[lightGrayView]-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(lightGrayView)]];

IB



A7502C31-F192-40AC-AD77-59FD3A8A9747.jpeg

上面3个都是一个意思,将灰色view的上、下、左、右分别约束到根视图的上margin、下margin、左margin、右margin。以4.7英寸为例


竖屏左右间距为16.0



F70411BA-601E-47C1-8D57-4B0EFF19EBE4.jpeg

横屏左右间距为20.0



DC50D195-6A8F-4C12-B1D3-6D965A3BE662.jpeg

iOS8的时代,不但需要设置separatorInset为UIEdgeInsetsZero,而且需要保证cell的layoutMargins为UIEdgeInsetsZero。


这里有个深坑,我在之前工程仅仅设置了separatorInset和layoutMargins为UIEdgeInsetsZero,确实有效,但是这次同样的设置,依旧留有15个单位长度,到底怎么回事。看了下文档,发现与layoutMargins一起加入iOS8豪华午餐的还有一个属性


/**
* preservesSuperviewLayoutMargins 表明当前视图是否保留父视图的margins,设置为YES,如果当前视图的margins小于父视图的margins,那么当前视图使用父视图的margins,默认为NO
*/

我们在之前灰色view上添加一个橙色view,设置如下


lightGrayView.layoutMargins = UIEdgeInsetsMake(10.0, 10.0, 10.0, 10.0);
lightGrayView.preservesSuperviewLayoutMargins = YES / NO;
UIView *orangeView = [[UIView alloc] init];
orangeView.backgroundColor = [UIColor orangeColor];
orangeView.translatesAutoresizingMaskIntoConstraints = NO;
[lightGrayView addSubview:orangeView];
[NSLayoutConstraint activateConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[orangeView]-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(orangeView)]];
[NSLayoutConstraint activateConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-[orangeView]-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(orangeView)]];

preservesSuperviewLayoutMargins = YES



IMG_0281.PNG

preservesSuperviewLayoutMargins = NO



IMG_0282.PNG

当preservesSuperviewLayoutMargins为YES时,灰色view的layoutMargins为UIEdgeInsetsMake(10.0, 16.0, 10.0, 16.0)
当preservesSuperviewLayoutMargins为NO时,灰色view的layoutMargins为UIEdgeInsetsMake(10.0, 10.0, 10.0, 10.0)


显然,这次留有15个单位长度应该是preservesSuperviewLayoutMargins为YES,保留了父视图的layoutMargins。这里顺带一提,分割线所处的结构


|--UITableView
|--UITableViewWrapperView 私有类,preservesSuperviewLayoutMargins默认为YES
|--UITableViewCell
|--UITableViewCellSeparatorView 私有类,分割线

那么到底什么原因造成上次cell的preservesSuperviewLayoutMargins为NO,这次cell的preservesSuperviewLayoutMargins为YES。思考了下,问题出在IB还是代码,上次table view是IB加载,这次table view是代码加载,通过控制台打印证实了我的推测。这时,我觉得还不对,分割线的父视图是cell,更准确说,cell如果是IB加载,preservesSuperviewLayoutMargins为NO,cell如果是代码加载,preservesSuperviewLayoutMargins为YES。


这就真的很坑爹了,cell的加载方式不同,造成默认设置不同。


我们新建一个CustomCell类,采用不同注册方式,看下控制台打印的结果


//from code
[self.tableView registerClass:[CustomCell class] forCellReuseIdentifier:CellIdentifier];
//from nib
[self.tableView registerNib:[UINib nibWithNibName:@"CustomCell" bundle:nil] forCellReuseIdentifier:CellIdentifier];

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
NSLog(@"preservesSuperviewLayoutMargins:%d",cell.preservesSuperviewLayoutMargins);
cell.textLabel.text = [NSString stringWithFormat:@"row%ld",indexPath.row];
return cell;
}

总结一下,分割线移除左边15个单位长度的步骤 :


separatorInset设置为UIEdgeInsetsZero,至于是table view还是table view cell,随你
table view cell的layoutMargins设置为UIEdgeInsetsZero
确定cell的加载方式,如果from nib,那么步骤1、2足矣。如果from code,那么两种处理方式
preservesSuperviewLayoutMargins设置为NO
table view的layoutMargins设置为UIEdgeInsetsZero

至此,妈妈再也不用担心我的分割线了。




相关阅读:
Top