由UITableView分割線說開去

RafaelFauld 8年前發布 | 13K 次閱讀 iOS開發 移動開發 UITableView

昨天遇到個老生常談的問題,UITableView分割線。我們知道,iPhone上默認的分割線左邊留有15個單位長度,想要去掉這個長度需要一番設置,網上雜七雜八很多,就不說了,主要分析的是問題原因。

說下歷史,iOS7之后,加入了separatorInset屬性,separatorInset雖然是UIEdgeInsets類型,但是只有左右生效,也就是說,separatorInset定義了分割線到左邊和右邊的距離。自從有了這個家伙,分割線就不會延伸到table view的邊界。顯然,iOS7的時代,只要設置了separatorInset為UIEdgeInsetsZero,那么皆大歡喜。這里可能有人注意到,UITableView和UITableViewCell都有這個屬性。區別在于 :

  1. UITableView設置的是所有cell的separatorInset,這有點類似rowHeight。我們可以通過table view設置全局cell的inset,也可以單獨為某個cell指定inset,后者的優先級大于前者。separatorInset默認為UIEdgeInsetsMake(0.0, 15.0, 0.0, 0.0)
  2. 除了全局設置,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個單位長度的步驟 :

  1. separatorInset設置為UIEdgeInsetsZero,至于是table view還是table view cell,隨你
  2. table view cell的layoutMargins設置為UIEdgeInsetsZero
  3. 確定cell的加載方式,如果from nib,那么步驟1、2足矣。如果from code,那么兩種處理方式
    • preservesSuperviewLayoutMargins設置為NO
    • table view的layoutMargins設置為UIEdgeInsetsZero

至此,媽媽再也不用擔心我的分割線了。

 

來自:http://www.jianshu.com/p/1274343055a7

 

 本文由用戶 RafaelFauld 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!