iOS — AutoLayout 還不會,你就累到死

花時間寫了個Demo,包含比較全面從UIView,UILabel,UIImageView的自適應(yīng)到UITextView,UITableView,UICollectionView,UIScrollView都有,可以下載學(xué)習(xí)一下。Demo地址
注意不配合demo也許會不知所云!

autolayoutdemo.gif

iOS8之后 使用AutoLayout是非常方便的,更何況iOS11都要發(fā)布了,不會還想著支持iOS7吧?。╥OS7也是支持autolayout的,只是有一些坑及與iOS8API的不同)。

iOS8以后,對于UIScrollView及UITableView,UICollectionViewCell的高度自適應(yīng)解決了我們一直頭疼的變高問題?。?!

如果你還不用,那么你覺得你是偷懶了,懶得去學(xué),其實你在開發(fā)中浪費了更多的時間與腦細胞。

UIView的自適應(yīng)

UIView可以根據(jù)其subviews自動適配自己的寬高,也就是如果其subviews能有明確的足夠的約束信息,那么view的寬高就可以被確定了

UILabel的自適應(yīng)

有時候如果想UILabel的文字增多變寬變高,要如何實現(xiàn)?其實UILabel是一個比較特殊的元素,只要設(shè)置了寬度約束,高度就會自適應(yīng)了(UILabel設(shè)置lines=0表示自動換行,具體數(shù)字是限制多少行)。如我們給UILabel設(shè)置約束寬度不大于200,然后動態(tài)改變它上邊的文字,它就自適應(yīng)了高度

AutoLayout是支持富文本的,也就是說我們設(shè)置約束的時候不限制其高度的話,是會自動適配的?。?!(@奮斗的小黃鳥 感謝你的反饋)

截屏內(nèi)容在demo中有完整代碼

不要怪我頁面搞得五顏六色,還不是為了讓控件更清晰嘛哈哈

UIImageView

看到網(wǎng)上好多人問如何處理圖片自適應(yīng)的問題,比如固定imageView的寬度,高度根據(jù)網(wǎng)絡(luò)獲取的image來動態(tài)改變。很遺憾,UIImageView并沒有自動布局。我們可以讓后臺返回寬高,或者請求到image對象后,根據(jù)image的size來獲取寬高。然后根據(jù)寬高的縮放比例來適配

[self.imageViewRef mas_makeConstraints:^(MASConstraintMaker *make) {
        make.edges.equalTo(self.contentView);
}];
customCell.imageViewRef.image = image;
[customCell.imageViewRef mas_updateConstraints:^(MASConstraintMaker *make) {
    make.height.mas_equalTo([UIScreen mainScreen].bounds.size.width * image.size.height / image.size.width);
 }];

UITextView的高度自適應(yīng)

要點:

  • UITextView的高度自適應(yīng)只需要不限制其height
  • scrollEnabled = NO 只有不可滾動才會自動擴展其高度
  • 若textView的內(nèi)容發(fā)生偏移,請看UITextView內(nèi)容偏移的問題處理
- (UITextView *)textView
{
    if (!_textView) {
        _textView = [[UITextView alloc] init];
        _textView.backgroundColor = [UIColor grayColor];
        _textView.scrollEnabled = NO;
        _textView.font = [UIFont systemFontOfSize:20];
    }
    return _textView;
}

- (void)makeContraints
{
    WEAKSELF
    [self.textView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.right.equalTo(weakSelf.view);
        make.top.mas_equalTo(100);
        make.height.greaterThanOrEqualTo(@100);
    }];
}

UITableViewCell的自適應(yīng)

*兼容iOS7 時
systemLayoutSizeFittingSize來取高。步驟是先在數(shù)據(jù)model中添加一個height的屬性用來緩存高,然后在table view的heightForRowAtIndexPath代理里static一個只初始化一次的Cell實例,然后根據(jù)model內(nèi)容填充數(shù)據(jù),最后根據(jù)cell的contentView的systemLayoutSizeFittingSize的方法獲取到cell的高,然后緩存給model的height

//還是那個熟悉的高度的代理,iOS8之后就少用它吧
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    static CustomCell *cell;
    //只初始化一次cell
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        cell = [tableView dequeueReusableCellWithIdentifier:"CustomCell"];
    });
    DataModel *model = self.dataArray[(NSUInteger) indexPath.row];
    [cell makeupData:model];

    if (model.cellHeight <= 0) {
        //使用systemLayoutSizeFittingSize獲取高度
        model.cellHeight = [cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height + 1;
    }
    return model.cellHeight;
}

在 iOS 8 中,cell 如果有一個確定的寬度/高度,autolayout 會自動根據(jù) cell 中的內(nèi)容計算出對應(yīng)的高度/寬度。

要讓 table view 的 cell 自適應(yīng)內(nèi)容,有幾個要點:

  1. 設(shè)置的 AutoLayout 約束必須讓 cell 的 contentView 知道如何自動延展。關(guān)鍵點是 contentView 的 4 個邊都要設(shè)置連接到內(nèi)容的約束,并且內(nèi)容是會動態(tài)改變尺寸的。

  2. UITableView 的 rowHeight 的值要設(shè)置為 UITableViewAutomaticDimension

  3. 實現(xiàn)tableView.estimatedRowHeight屬性或者 estimatedHeightForRowAtIndexPath代理。

//需要設(shè)置的兩句代碼:一點計算 cell 高度的代碼都沒有??!連heightForRowAtIndexPath都不用實現(xiàn)
 _tableView.estimatedRowHeight = 80;
_tableView.rowHeight = UITableViewAutomaticDimension;

UITableView-FDTemplateLayoutCell 是一個有名的cell高度緩存的庫,可以減少系統(tǒng)計算行高的次數(shù),讓UITableView滑動更流暢。

UITextView 在Cell中的高度自適應(yīng)

需要textView高度自適應(yīng)的需求時常表現(xiàn)為---UITextView嵌套在Cell中

//  TextViewCell.m
//cell初始化
-(instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
    self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
    if (self) {
        [self.contentView addSubview:self.textView];
         __weak typeof(self) weakSelf = self;
        [self.textView mas_makeConstraints:^(MASConstraintMaker *make) {
            make.edges.equalTo(weakSelf.contentView);
        }];
    }
    return self;
}

-(UITextView *)textView
{
    if (!_textView) {
        _textView = [[UITextView alloc]init];
        _textView.backgroundColor = [UIColor greenColor];
        _textView.font = [UIFont systemFontOfSize:20];
        //scrollEnabled 必須設(shè)置為NO,否則sizeToFit適配不了
        _textView.scrollEnabled = NO;
        _textView.delegate = self;
    }
    return _textView;
}

//實現(xiàn)變高的關(guān)鍵所在
-(void)textViewDidChange:(UITextView *)textView
{
    [textView sizeToFit];
    UITableView *tableView = [self tableView];
    [tableView beginUpdates];
    [tableView endUpdates];
}

- (UITableView *)tableView
{
    UIView *tableView = self.superview;
    
    while (![tableView isKindOfClass:[UITableView class]] && tableView) {
        tableView = tableView.superview;
    }
    
    return (UITableView *)tableView;
}

//tableView的代碼
-(UITableView *)tableView
{
    if (!_tableView) {
        _tableView = [[UITableView alloc]initWithFrame:self.view.bounds style:UITableViewStylePlain];
        _tableView.delegate = self;
        _tableView.dataSource = self;
        
        [_tableView registerClass:[TextViewCell class] forCellReuseIdentifier:@"TextViewCell"];

//自適應(yīng)cell需要的代碼
        _tableView.estimatedRowHeight = 40;
        _tableView.rowHeight = UITableViewAutomaticDimension;

    }
    return _tableView;
}

UICollectionViewCell的自適應(yīng)

在 collection view 中也能讓 cell 自適應(yīng)內(nèi)容大小,如果 UICollectionView 的 layout 是一個 UICollectionViewFlowLayout,只需要將 layout.itemSize = ... 改成 layout.estimatedItemSize = ...。 只要設(shè)置了 layout 的 estimatedItemSize,collection view 就會根據(jù) cell 里面的 autolayout 約束去確定cell 的大小

原理:

  1. collection view 根據(jù) layout 的 estimatedItemSize 算出估計的 contentSize,有了 contentSize collection view 就開始顯示

  2. collection view 在顯示的過程中,即將被顯示的 cell 根據(jù) autolayout 的約束算出自適應(yīng)內(nèi)容的 size

  3. layout 從 collection view 里獲取更新過的 size attribute

  4. layout 返回最終的 size attribute 給 collection view

  5. collection 使用這個最終的 size attribute 展示 cell

//只需要實現(xiàn)估算高度,cell會根據(jù)其subviews自動獲取大小。
//也就是說cell的subviews需要有足夠的約束信息推斷出cell的size

layout.estimatedItemSize = CGSizeMake(SCREEN_WIDTH/2 , 150);

比如實現(xiàn):

collectionview.gif

當然你也可以把它當tableview用??

UIScrollView的自適應(yīng)

UIScrollView也可以自適應(yīng),自適應(yīng)UIScrollView就不需要我們再計算contentSize了。
但是要注意的是:UIScrollView的contentSize是根據(jù)它的subView來計算的,也就是其subViews自身必須能夠確定大小。

UIView *container = [UIView new];
[scrollView addSubview:container];
[container mas_makeConstraints:^(MASConstraintMaker *make) {
    make.edges.equalTo(scrollView);
    //這里的寬度的意思是:contentSize的寬固定,高度變化,豎直方向滑動
    make.width.equalTo(scrollView);
}];

總結(jié):

AutoLayout 已經(jīng)是勢在必行了,曾經(jīng)它增加了代碼量,使很多人還停留在frame不舍得轉(zhuǎn)過來,現(xiàn)在呢,它可以極大的方便開發(fā)者,還可以省很多代碼。只要多加練習(xí),對于iOS的布局就不會再那么恐怖了!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容