iOS - 关于UIScrollView的自动布局那些事儿

简介

本文主要是针对UIScrollView利用Masonry框架来自动布局,因为UIScrollView可以滑动,所以本身布局跟一般的UIView不太一样,然后楼主之前面试的时候也有人问过楼主这个问题,所以楼主想简单总结一下方便你我他它。关于Masonry这个框架,相信大家也都在用它,可以去github上面下载,它里面的各种demo也是非常有用的。

我们设置约束用到的几个方法简单介绍:

一.mas_makeConstraints:设置约束,如果你的视图不需要根据什么情况更新约束,只需要设置一次的话,请使用它。

错误示范:多次调用

- (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 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];
}

结果:

第一次添加约束时,约束数组是酱紫的:


iOS - 关于UIScrollView的自动布局那些事儿_第1张图片
1.png

这时候约束数组的count为0,是正确的,因为我们之前没有设置过约束,点击测试按钮,再添加一次约束,结果是酱紫的:


iOS - 关于UIScrollView的自动布局那些事儿_第2张图片
2..png

这时候,约束数组的count值是4,也是正确的,因为我们添加过一次了,可是这次点击过后,约束数组的count就会变成8了:
iOS - 关于UIScrollView的自动布局那些事儿_第3张图片
3.png

很明显,点击一次就会重复累加而不会清除之前的约束,后果可想而知。。。说到这里,大家应该理解mas_makeConstraints的使用场景了。

二.mas_updateConstraints:更新约束,如果你的布局需要根据具体情况来更新子控件的约束,那么请使用它。mas_updateConstraints是对比该对象之前的约束数组,添加过的约束就直接修改它的值,没有添加过的约束就添加上。

iOS - 关于UIScrollView的自动布局那些事儿_第4张图片
4.png

结论:无论你点多少次都不重复添加约束,感觉 mas_updateConstraints真是棒棒哒。

三.mas_remakeConstraints:在mas_makeConstraints的基础上会清除掉之前所有的约束,再重新添加新的约束。

UIScrollView的自动布局问题

在利用自动布局来布局UIScrollView时,一般都会在上面添加一个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);
    }];
    
  // 必须要调用
    [super updateViewConstraints];
}

scrollView的contentSize要根据containerView的高度来设置,containView的高度又要根据它内部的子控件来设置,make.bottom.equalTo(self.containerView)确定containerView的高度,make.width.equalTo(self.scrollView)确定containerview的宽度。

模拟复杂环境下UISCrollView的自动布局

现假如有这么一种需求:一个控制器里面有多个视图A B C D,而每个视图的内容要去请求网络获取数据才能确定高度,但是对于这几个网络请求谁先成功获取数据计算到高度是不确定的,如何布局?

代码片段

#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后网络请求数据回来了,oneLabel根据数据获得了高度。
   dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
       self.oneHeight = 80;
       [self.view setNeedsUpdateConstraints];
   });
   
   // 模拟6s后网络请求数据回来了,fourLabel根据数据获得了高度。
   dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(6 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
       self.fourHeight = 100;
       [self.view setNeedsUpdateConstraints];
   });
   
   // 模拟9s后网络请求数据回来了,twoLabel根据数据获得了高度。
   dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(9 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
       self.twoHeight = 150;
       [self.view setNeedsUpdateConstraints];
   });
   
   // 模拟12s后网络请求数据回来了,threeLabel根据数据获得了高度。
   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];
}

结果:楼主做的这个gif图跟实际有点差别,将就看

5.gif

你可能感兴趣的:(iOS - 关于UIScrollView的自动布局那些事儿)