iOS - 關(guān)于UIScrollView的自動(dòng)布局那些事兒

簡(jiǎn)介

本文主要是針對(duì)UIScrollView利用Masonry框架來自動(dòng)布局,因?yàn)閁IScrollView可以滑動(dòng),所以本身布局跟一般的UIView不太一樣,然后樓主之前面試的時(shí)候也有人問過樓主這個(gè)問題,所以樓主想簡(jiǎn)單總結(jié)一下方便你我他它。關(guān)于Masonry這個(gè)框架,相信大家也都在用它,可以去github上面下載,它里面的各種demo也是非常有用的。

我們?cè)O(shè)置約束用到的幾個(gè)方法簡(jiǎn)單介紹:

一.mas_makeConstraints:設(shè)置約束,如果你的視圖不需要根據(jù)什么情況更新約束,只需要設(shè)置一次的話,請(qǐng)使用它。

錯(cuò)誤示范:多次調(diào)用

- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor whiteColor];
    [self.view addSubview:self.scrollView];
    [self.scrollView addSubview:self.containerView];
    [self.containerView addSubview:self.testButton];
    [self.containerView addSubview:self.testView]; 
    // 設(shè)置約束
    [self setupConstraints];
}

- (void)setupConstraints {
    [self.scrollView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(self.view).offset(64);
        make.left.right.bottom.equalTo(self.view);
    }];
    
    [self.containerView mas_makeConstraints:^(MASConstraintMaker *make) {
         make.edges.equalTo(self.scrollView);
         make.width.equalTo(self.scrollView);
    }];
    
    [self.testButton mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(self.containerView).offset(100);
        make.left.equalTo(self.containerView).offset(50);
        make.right.equalTo(self.containerView).offset(-50);
        make.height.equalTo(@40);
    }];
    
    [self.testView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(self.testButton.mas_bottom).offset(200);
        make.left.right.equalTo(self.containerView);
        make.height.equalTo(@400);
        make.bottom.equalTo(self.containerView);
    }];
}

- (void)testButtonClick {
    [self setupConstraints];
}

結(jié)果:

第一次添加約束時(shí),約束數(shù)組是醬紫的:


1.png

這時(shí)候約束數(shù)組的count為0,是正確的,因?yàn)槲覀冎皼]有設(shè)置過約束,點(diǎn)擊測(cè)試按鈕,再添加一次約束,結(jié)果是醬紫的:


2..png

這時(shí)候,約束數(shù)組的count值是4,也是正確的,因?yàn)槲覀兲砑舆^一次了,可是這次點(diǎn)擊過后,約束數(shù)組的count就會(huì)變成8了:
3.png

很明顯,點(diǎn)擊一次就會(huì)重復(fù)累加而不會(huì)清除之前的約束,后果可想而知。。。說到這里,大家應(yīng)該理解mas_makeConstraints的使用場(chǎng)景了。

二.mas_updateConstraints:更新約束,如果你的布局需要根據(jù)具體情況來更新子控件的約束,那么請(qǐng)使用它。mas_updateConstraints是對(duì)比該對(duì)象之前的約束數(shù)組,添加過的約束就直接修改它的值,沒有添加過的約束就添加上。

4.png

結(jié)論:無論你點(diǎn)多少次都不重復(fù)添加約束,感覺mas_updateConstraints真是棒棒噠。

三.mas_remakeConstraints:在mas_makeConstraints的基礎(chǔ)上會(huì)清除掉之前所有的約束,再重新添加新的約束。

UIScrollView的自動(dòng)布局問題

在利用自動(dòng)布局來布局UIScrollView時(shí),一般都會(huì)在上面添加一個(gè)UIView的子控件,來正確布局:

代碼片段

- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor whiteColor];
    [self.view addSubview:self.scrollView];
    [self.scrollView addSubview:self.containerView];
    [self.containerView addSubview:self.testButton];
    [self.containerView addSubview:self.testView];
    
    [self.view setNeedsUpdateConstraints];
}

- (void)updateViewConstraints {
    [self.scrollView mas_updateConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(self.view).offset(64);
        make.left.right.bottom.equalTo(self.view);
    }];
    
    [self.containerView mas_updateConstraints:^(MASConstraintMaker *make) {
         make.edges.equalTo(self.scrollView);
        // 確定containerView的寬度
         make.width.equalTo(self.scrollView);
    }];
    
    [self.testButton mas_updateConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(self.containerView).offset(100);
        make.left.equalTo(self.containerView).offset(50);
        make.right.equalTo(self.containerView).offset(-50);
        make.height.equalTo(@40);
    }];
    
    [self.testView mas_updateConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(self.testButton.mas_bottom).offset(200);
        make.left.right.equalTo(self.containerView);
        make.height.equalTo(@400);
        // 確定containerView的高度
        make.bottom.equalTo(self.containerView);
    }];
    
  // 必須要調(diào)用
    [super updateViewConstraints];
}

scrollView的contentSize要根據(jù)containerView的高度來設(shè)置,containView的高度又要根據(jù)它內(nèi)部的子控件來設(shè)置,make.bottom.equalTo(self.containerView)確定containerView的高度,make.width.equalTo(self.scrollView)確定containerview的寬度。

模擬復(fù)雜環(huán)境下UISCrollView的自動(dòng)布局

現(xiàn)假如有這么一種需求:一個(gè)控制器里面有多個(gè)視圖A B C D,而每個(gè)視圖的內(nèi)容要去請(qǐng)求網(wǎng)絡(luò)獲取數(shù)據(jù)才能確定高度,但是對(duì)于這幾個(gè)網(wǎng)絡(luò)請(qǐng)求誰先成功獲取數(shù)據(jù)計(jì)算到高度是不確定的,如何布局?

代碼片段

#pragma mark -- life cycle
- (void)viewDidLoad {
   [super viewDidLoad];

   self.oneHeight = 0;
   self.twoHeight = 0;
   self.threeHeight = 0;
   self.fourHeight = 0;
   
   [self.view addSubview:self.scrollView];
   [self.scrollView addSubview:self.containerView];
   
   [self.containerView addSubview:self.oneLabel];
   [self.containerView addSubview:self.twoLabel];
   [self.containerView addSubview:self.threeLabel];
   [self.containerView addSubview:self.fourLabel];
   
   [self.view setNeedsUpdateConstraints];
   
   // 模擬3s后網(wǎng)絡(luò)請(qǐng)求數(shù)據(jù)回來了,oneLabel根據(jù)數(shù)據(jù)獲得了高度。
   dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
       self.oneHeight = 80;
       [self.view setNeedsUpdateConstraints];
   });
   
   // 模擬6s后網(wǎng)絡(luò)請(qǐng)求數(shù)據(jù)回來了,fourLabel根據(jù)數(shù)據(jù)獲得了高度。
   dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(6 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
       self.fourHeight = 100;
       [self.view setNeedsUpdateConstraints];
   });
   
   // 模擬9s后網(wǎng)絡(luò)請(qǐng)求數(shù)據(jù)回來了,twoLabel根據(jù)數(shù)據(jù)獲得了高度。
   dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(9 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
       self.twoHeight = 150;
       [self.view setNeedsUpdateConstraints];
   });
   
   // 模擬12s后網(wǎng)絡(luò)請(qǐng)求數(shù)據(jù)回來了,threeLabel根據(jù)數(shù)據(jù)獲得了高度。
   dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(12 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
       self.threeHeight = 60;
       [self.view setNeedsUpdateConstraints];
   });
   
   
}

- (void)updateViewConstraints {
   [self.scrollView mas_updateConstraints:^(MASConstraintMaker *make) {
       make.edges.equalTo(self.view);
   }];
   
   [self.containerView mas_updateConstraints:^(MASConstraintMaker *make) {
       make.edges.equalTo(self.scrollView);
       make.width.equalTo(self.scrollView);
   }];
   
   [self.oneLabel mas_updateConstraints:^(MASConstraintMaker *make) {
       make.top.equalTo(self.containerView).offset(50);
       make.left.right.equalTo(self.containerView);
       make.height.equalTo(@(self.oneHeight));
   }];
   
   [self.twoLabel mas_updateConstraints:^(MASConstraintMaker *make) {
       make.top.equalTo(self.oneLabel.mas_bottom);
       make.left.right.equalTo(self.containerView);
       make.height.equalTo(@(self.twoHeight));
   }];
   
   [self.threeLabel mas_updateConstraints:^(MASConstraintMaker *make) {
       make.top.equalTo(self.twoLabel.mas_bottom);
       make.left.right.equalTo(self.containerView);
       make.height.equalTo(@(self.threeHeight));
   }];
   
   [self.fourLabel mas_updateConstraints:^(MASConstraintMaker *make) {
       make.top.equalTo(self.threeLabel.mas_bottom);
       make.left.right.equalTo(self.containerView);
       make.height.equalTo(@(self.fourHeight));
       make.bottom.equalTo(self.containerView);
   }];
   
   [super updateViewConstraints];
}

結(jié)果:樓主做的這個(gè)gif圖跟實(shí)際有點(diǎn)差別,將就看

5.gif
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容