Coding源码学习第四部分(Masonry介绍与使用(三))

时间:2021-10-07 22:39:10

接上篇继续进行Masonry 的学习。

(12)tableViewCell 布局

 #import "TableViewController.h"
#import "TestTableViewCell.h" @interface TableViewController ()<UITableViewDelegate, UITableViewDataSource> @property(nonatomic, strong) UITableView *tableView;
@property(nonatomic, strong) NSMutableArray *dataSource; @end @implementation TableViewController - (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view. self.tableView = [[UITableView alloc] init];
self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
self.tableView.delegate = self;
self.tableView.dataSource = self;
[self.view addSubview:self.tableView];
[self.tableView mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.mas_equalTo(self.view);
}]; for (NSUInteger i = ; i < ; ++i) {
TestModel *model = [[TestModel alloc] init];
model.title = @"测试标题, 可能很长很长,反正随便写着先吧";
model.desc = @"描述内容通常都是很长很长的,描述内容通常都是很长很长的,描述内容通常都是很长很长的,描述内容通常都是很长很长的,描述内容通常都是很长很长的,描述内容通常都是很长很长的,描述内容通常都是很长很长的,描述内容通常都是很长很长的,描述内容通常都是很长很长的,描述内容通常都是很长很长的,描述内容通常都是很长很长的,";
[self.dataSource addObject:model];
}
[self.tableView reloadData];
} - (NSMutableArray *)dataSource {
if (_dataSource == nil) {
_dataSource = [[NSMutableArray alloc] init];
}
return _dataSource;
} #pragma mark - UITableViewDataSource
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.dataSource.count;
} - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellIdentifier = @"CellIdentifier";
TestTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (!cell) {
cell = [[TestTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
}
cell.indexPath = indexPath;
cell.block = ^(NSIndexPath *path) {
[tableView reloadRowsAtIndexPaths:@[path] withRowAnimation:UITableViewRowAnimationFade];
};
TestModel *model = [self.dataSource objectAtIndex:indexPath.row];
[cell configCellWithModel:model];
return cell;
} - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
TestModel *model = [self.dataSource objectAtIndex:indexPath.row];
return [TestTableViewCell heightWithModel:model];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
} /*
#pragma mark - Navigation // In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/ @end

Coding源码学习第四部分(Masonry介绍与使用(三))

我们来看看这个计算行高的代码,看起来是不是很像配置数据的代理方法呢?

 - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
TestModel *model = [self.dataSource objectAtIndex:indexPath.row];
return [TestTableViewCell heightWithModel:model];
}

我们看看TestCell的声明,提供了一个计算行高的类方法:

 #import <UIKit/UIKit.h>

 @interface TestModel : NSObject

 @property(nonatomic, copy) NSString *title;
@property(nonatomic, copy) NSString *desc;
@property(nonatomic, assign) BOOL isExpanded; @end typedef void (^TestBlock)(NSIndexPath *indexPath); @interface TestTableViewCell : UITableViewCell @property(nonatomic, strong) UILabel *titleLabel;
@property(nonatomic, strong) UILabel *descLabel;
@property(nonatomic, strong) NSIndexPath *indexPath; @property(nonatomic, copy) TestBlock block; - (void)configCellWithModel:(TestModel *)model;
+ (CGFloat)heightWithModel:(TestModel *)model; @end

我们看一下计算行高的实现:

 + (CGFloat)heightWithModel:(TestModel *)model {
TestTableViewCell *cell = [[TestTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@""];
[cell configCellWithModel:model];
[cell layoutIfNeeded];
CGRect frame = cell.descLabel.frame;
return frame.origin.y + frame.size.height + ;
}

我们只是创建了一个cell然后配置数据,然后调用layoutIfNeeded更新约束,以便获取到frame。当我们获取到以后,我们就可以计算出最后的cell真正的高度了。

(13)ScrollView循环布局

 #import "ScrollViewController.h"

 @interface ScrollViewController ()

 @property (nonatomic, strong) UIScrollView *scrollView;

 @end

 @implementation ScrollViewController

 - (void)viewDidLoad {
[super viewDidLoad]; self.scrollView = [[UIScrollView alloc] init];
self.scrollView.pagingEnabled = NO;
[self.view addSubview:self.scrollView];
self.scrollView.backgroundColor = [UIColor lightGrayColor]; CGFloat screenWidth = [UIScreen mainScreen].bounds.size.width;
UILabel *lastLabel = nil;
for (NSUInteger i = ; i < ; ++i) {
UILabel *label = [[UILabel alloc] init];
label.numberOfLines = ;
label.layer.borderColor = [UIColor greenColor].CGColor;
label.layer.borderWidth = 2.0;
label.text = [self randomText]; // We must preferredMaxLayoutWidth property for adapting to iOS6.0
label.preferredMaxLayoutWidth = screenWidth - ;
label.textAlignment = NSTextAlignmentLeft;
label.textColor = [self randomColor];
[self.scrollView addSubview:label]; [label mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.mas_equalTo();
make.right.mas_equalTo(self.view).offset(-); if (lastLabel) {
make.top.mas_equalTo(lastLabel.mas_bottom).offset();
} else {
make.top.mas_equalTo(self.scrollView).offset();
}
}]; lastLabel = label;
} [self.scrollView mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.mas_equalTo(self.view); // 让scrollview的contentSize随着内容的增多而变化
make.bottom.mas_equalTo(lastLabel.mas_bottom).offset();
}];
} - (UIColor *)randomColor {
CGFloat hue = ( arc4random() % / 256.0 ); // 0.0 to 1.0
CGFloat saturation = ( arc4random() % / 256.0 ) + 0.5; // 0.5 to 1.0, away from white
CGFloat brightness = ( arc4random() % / 256.0 ) + 0.5; // 0.5 to 1.0, away from black
return [UIColor colorWithHue:hue saturation:saturation brightness:brightness alpha:];
} - (NSString *)randomText {
CGFloat length = arc4random() % + ; NSMutableString *str = [[NSMutableString alloc] init];
for (NSUInteger i = ; i < length; ++i) {
[str appendString:@"测试数据很长,"];
} return str;
} @end

Coding源码学习第四部分(Masonry介绍与使用(三))

对于循环创建,我们需要记录下一个视图所依赖的控件,这里使用了lastLabel来记录。

我们要想让scrollviewcontentSize随内容的变化而变化,那么就我们一定要添加注意添加约束:

     [self.scrollView mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.mas_equalTo(self.view); // 让scrollView 的contentSize 随着内容的增多而变化
make.bottom.mas_equalTo(lastLabel.mas_bottom).offset();
}];

对于scrollviewtableview,我们不能使用bottom来计算其高,因为这个属性对于scrollviewtableview来说,不用用来计算高度的,而是用于计算contentSize.height的。我们要想随内容而变化,以便可滚动查看,就必须设置bottom约束。

(14)复杂ScrollerView 布局

 #import "ScrollViewComplexController.h"

 @interface ScrollViewComplexController ()

 @property (nonatomic, strong) UIScrollView *scrollView;
@property (nonatomic, strong) NSMutableArray *expandStates; @end @implementation ScrollViewComplexController - (void)viewDidLoad {
[super viewDidLoad]; self.scrollView = [[UIScrollView alloc] init];
self.scrollView.pagingEnabled = NO;
[self.view addSubview:self.scrollView];
self.scrollView.backgroundColor = [UIColor lightGrayColor]; CGFloat screenWidth = [UIScreen mainScreen].bounds.size.width;
UILabel *lastLabel = nil;
for (NSUInteger i = ; i < ; ++i) {
UILabel *label = [[UILabel alloc] init];
label.numberOfLines = ;
label.layer.borderColor = [UIColor greenColor].CGColor;
label.layer.borderWidth = 2.0;
label.text = [self randomText];
label.userInteractionEnabled = YES;
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onTap:)];
[label addGestureRecognizer:tap]; // We must preferredMaxLayoutWidth property for adapting to iOS6.0
label.preferredMaxLayoutWidth = screenWidth - ; // iOS 6 的用法
label.textAlignment = NSTextAlignmentLeft;
label.textColor = [self randomColor];
[self.scrollView addSubview:label]; [label mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.mas_equalTo();
make.right.mas_equalTo(self.view).offset(-); if (lastLabel) {
make.top.mas_equalTo(lastLabel.mas_bottom).offset();
} else {
make.top.mas_equalTo(self.scrollView).offset();
} make.height.mas_equalTo();
}]; lastLabel = label; [self.expandStates addObject:[@[label, @(NO)] mutableCopy]];
} [self.scrollView mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.mas_equalTo(self.view); // 让scrollview的contentSize随着内容的增多而变化
make.bottom.mas_equalTo(lastLabel.mas_bottom).offset();
}];
} - (NSMutableArray *)expandStates {
if (_expandStates == nil) {
_expandStates = [[NSMutableArray alloc] init];
} return _expandStates;
} - (UIColor *)randomColor {
CGFloat hue = ( arc4random() % / 256.0 ); // 0.0 to 1.0
CGFloat saturation = ( arc4random() % / 256.0 ) + 0.5; // 0.5 to 1.0, away from white
CGFloat brightness = ( arc4random() % / 256.0 ) + 0.5; // 0.5 to 1.0, away from black
return [UIColor colorWithHue:hue saturation:saturation brightness:brightness alpha:];
} - (NSString *)randomText {
CGFloat length = arc4random() % + ; NSMutableString *str = [[NSMutableString alloc] init];
for (NSUInteger i = ; i < length; ++i) {
[str appendString:@"测试数据很长,"];
} return str;
} - (void)onTap:(UITapGestureRecognizer *)sender {
UIView *tapView = sender.view; NSUInteger index = ;
for (NSMutableArray *array in self.expandStates) {
UILabel *view = [array firstObject]; if (view == tapView) {
NSNumber *state = [array lastObject]; // 当前是展开状态的话,就收缩
if ([state boolValue] == YES) {
[view mas_updateConstraints:^(MASConstraintMaker *make) {
make.height.mas_equalTo();
}];
} else {
UIView *preView = nil;
UIView *nextView = nil; if (index - < self.expandStates.count && index >= ) {
preView = [[self.expandStates objectAtIndex:index - ] firstObject];
} if (index + < self.expandStates.count) {
nextView = [[self.expandStates objectAtIndex:index + ] firstObject];
} [view mas_remakeConstraints:^(MASConstraintMaker *make) {
if (preView) {
make.top.mas_equalTo(preView.mas_bottom).offset();
} else {
make.top.mas_equalTo();
} make.left.mas_equalTo();
make.right.mas_equalTo(self.view).offset(-);
}]; if (nextView) {
[nextView mas_updateConstraints:^(MASConstraintMaker *make) {
make.top.mas_equalTo(view.mas_bottom).offset();
}];
}
} [array replaceObjectAtIndex: withObject:@(!state.boolValue)]; [self.view setNeedsUpdateConstraints];
[self.view updateConstraintsIfNeeded]; [UIView animateWithDuration:0.35 animations:^{
[self.view layoutIfNeeded];
} completion:^(BOOL finished) { }];
break;
} index++;
}
} @end

Coding源码学习第四部分(Masonry介绍与使用(三))

当我们要收起的时候,只是简单地设置其高度的约束为40,但是当我们要展开时,实现起来就相对麻烦了。因为我们需要重新添加约束,要重新给所点击的视图添加约束,就需要知道前一个依赖视图和后一个依赖视图的约束,以便将相关联的都更新约束。

当我们更新所点击的视图时,我们通过判断是否有前一个依赖视图来设置顶部约束:

                     if (preView) {
make.top.mas_equalTo(preView.mas_bottom).offset();
} else {
make.top.mas_equalTo();
}

除了这个之外,我们也需要更新后一个视图的约束,因为我们对所点击的视图调用了mas_remakeConstraints方法,就会移除其之前所添加的所有约束,所以我们必须重新将后者对当前点击的视图的依赖重新添加上去:

                 if (nextView) {
[nextView mas_updateConstraints:^(MASConstraintMaker *make) {
make.top.mas_equalTo(view.mas_bottom).offset();
}];
}

(15)scrollView 实战场景

 #import "HeaderFooterViewController.h"

 @interface HeaderFooterViewController ()

 @property(nonatomic, strong) UIScrollView *scrollView;
@property(nonatomic, strong) UITableView *tableView; @end @implementation HeaderFooterViewController - (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
[self configTableView];
} - (void)configScrollView {
self.scrollView = [[UIScrollView alloc] init];
[self.view addSubview:self.scrollView];
[self.scrollView mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.mas_equalTo(self.view);
}]; UIView *headerView = [[UIView alloc] init];
[self.scrollView addSubview:headerView]; UIImageView *imgView = [[UIImageView alloc] init];
[headerView addSubview:imgView];
imgView.backgroundColor = [UIColor greenColor];
imgView.layer.cornerRadius = ;
imgView.layer.masksToBounds = YES;
imgView.layer.borderWidth = 0.5;
imgView.layer.borderColor = [UIColor redColor].CGColor; CGFloat screenWidth = [UIScreen mainScreen].bounds.size.width; UILabel *tipLabel = [[UILabel alloc] init];
tipLabel.text = @"这里是提示信息,通常会比较长,可能会超过两行。为了适配6.0,我们需要指定preferredMaxLayoutWidth,但是要注意,此属性一旦设置,不是只在6.0上生效,任意版本的系统的都有作用,因此此值设置得一定要准备,否则计算结果会不正确。我们一定要注意,不能给tableview的tableHeaderView和tableFooterView添加约束,在6.0及其以下版本上会crash,其它版本没有";
tipLabel.textAlignment = NSTextAlignmentCenter;
tipLabel.textColor = [UIColor blackColor];
tipLabel.backgroundColor = [UIColor clearColor];
tipLabel.numberOfLines = ;
tipLabel.preferredMaxLayoutWidth = screenWidth - ;
[headerView addSubview:tipLabel]; UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
[button setTitle:@"show Detail" forState:UIControlStateNormal];
[button setTitleColor: [UIColor whiteColor] forState:UIControlStateNormal];
[button setBackgroundColor:[UIColor blueColor]];
button.layer.cornerRadius = ;
button.clipsToBounds = YES;
button.layer.masksToBounds = YES; [headerView addSubview:button]; [headerView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.top.mas_equalTo();
make.width.mas_equalTo(self.view);
make.bottom.mas_equalTo(button.mas_bottom).offset().priorityLow();
make.height.greaterThanOrEqualTo(self.view);
}]; [imgView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.mas_equalTo();
make.centerX.mas_equalTo(headerView);
make.width.height.mas_equalTo();
}]; [tipLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.mas_equalTo();
make.right.mas_equalTo(-);
make.top.mas_equalTo(imgView.mas_bottom).offset();
}]; [button mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.mas_greaterThanOrEqualTo(tipLabel.mas_bottom).offset();
make.left.mas_equalTo();
make.right.mas_equalTo(-);
make.height.mas_equalTo();
}]; [self.scrollView mas_makeConstraints:^(MASConstraintMaker *make) {
make.bottom.mas_equalTo(button.mas_bottom).offset().priorityLow();
make.bottom.mas_greaterThanOrEqualTo(self.view);
}];
} - (void)configTableView {
if (self.tableView != nil) {
return;
}
self.tableView = [[UITableView alloc] init];
[self.view addSubview:self.tableView];
[self.tableView mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.mas_equalTo(self.view);
}]; NSArray *array = [self headerViewWithHeight:self.view.frame.size.height addToView:self.view];
UIView *headerView = [array firstObject];
[headerView layoutIfNeeded];
UIButton *button = [array lastObject];
CGFloat h = button.frame.size.height + button.frame.origin.y + ;
h = h < self.view.frame.size.height ? self.view.frame.size.height : h; [headerView removeFromSuperview];
[self headerViewWithHeight:h addToView:nil];
} - (NSArray *)headerViewWithHeight:(CGFloat)height addToView:(UIView *)toView {
// 注意, 绝对不能给tableHeaderView 直接添加约束, 必闪退
UIView *headerView = [[UIView alloc] initWithFrame:CGRectMake(, , self.view.frame.size.width, height)]; if (toView) {
[toView addSubview:headerView];
} else {
self.tableView.tableHeaderView = headerView;
} UIImageView *imgView = [[UIImageView alloc] init];
[headerView addSubview:imgView];
imgView.backgroundColor = [UIColor greenColor];
imgView.layer.cornerRadius = ;
imgView.layer.masksToBounds = YES;
imgView.layer.borderWidth = 0.5;
imgView.layer.borderColor = [UIColor redColor].CGColor; CGFloat screentWidth = [UIScreen mainScreen].bounds.size.width; UILabel *tipLabel = [[UILabel alloc] init];
tipLabel.text = @"这里是提示信息,通常会比较长,可能会超过两行。为了适配6.0,我们需要指定preferredMaxLayoutWidth,但是要注意,此属性一旦设置,不是只在6.0上生效,任意版本的系统的都有作用,因此此值设置得一定要准备,否则计算结果会不正确。我们一定要注意,不能给tableview的tableHeaderView和tableFooterView添加约束,在6.0及其以下版本上会crash,其它版本没有";
tipLabel.textAlignment = NSTextAlignmentCenter;
tipLabel.textColor = [UIColor blackColor];
tipLabel.backgroundColor = [UIColor clearColor];
tipLabel.numberOfLines = ;
tipLabel.preferredMaxLayoutWidth = screentWidth - ;
[headerView addSubview:tipLabel]; UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
[button setTitle:@"Show detail" forState:UIControlStateNormal];
[button setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
[button setBackgroundColor:[UIColor blueColor]];
button.layer.cornerRadius = ;
button.clipsToBounds = YES;
button.layer.masksToBounds = YES; [headerView addSubview:button]; [imgView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.mas_equalTo();
make.centerX.mas_equalTo(headerView);
make.width.height.mas_equalTo();
}]; [tipLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.mas_equalTo(self.view).offset();
make.right.mas_equalTo(self.view).offset(-);
make.top.mas_equalTo(imgView.mas_bottom).offset();
}]; [button mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.mas_greaterThanOrEqualTo(tipLabel.mas_bottom).offset();
make.left.mas_equalTo(tipLabel);
make.right.mas_equalTo(tipLabel);
make.height.mas_equalTo();
}]; return @[headerView, button];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
} /*
#pragma mark - Navigation // In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/ @end

Coding源码学习第四部分(Masonry介绍与使用(三))

这里只是说说如何设置约束以保证按钮的位置始终是有一定距离的:

make.bottom.mas_equalTo(button.mas_bottom).offset().priorityLow(); make.bottom.mas_greaterThanOrEqualTo(self.view);

我们设置了scrollviewbottombutton的底部+80个像素,但是其优先级为最低。然后又设置了scrollviewbottom要大于或者等于self.view,也就是说contentSize.height至少为屏高。

温馨提示:对于UILabel,如果需要兼容到iOS6.0,一定要设置preferredMaxLayoutWidth属性值,并且其值一定要与约束所生成的宽一样,否则就会出现显示不完全的问题。

Masonry 使用注意:

简要

自动布局最重要的是约束:UI元素间关系的数学表达式。约束包括尺寸、由优先级和阈值管理的相对位置。它们是添加剂,可能导致约束冲突 、约束不足造成布局无法确定 。这两种情况都会产生异常。

使用前:AutoLayout关于更新的几个方法的区别

  • setNeedsLayout:告知页面需要更新,但是不会立刻开始更新。执行后会立刻调用layoutSubviews。
  • layoutIfNeeded:告知页面布局立刻更新。所以一般都会和setNeedsLayout一起使用。如果希望立刻生成新的frame需要调用此方法,利用这点一般布局动画可以在更新布局后直接使用这个方法让动画生效。
  • layoutSubviews:系统重写布局
  • setNeedsUpdateConstraints:告知需要更新约束,但是不会立刻开始
  • updateConstraintsIfNeeded:告知立刻更新约束
  • updateConstraints:系统更新约束

前面没有提到的使用细节:

  • multipler属性表示约束值为约束对象的乘因数, dividedBy属性表示约束值为约束对象的除因数,可用于设置view的宽高比
  • priorityLow()设置约束优先级
  • #define MAS_SHORTHAND_GLOBALS使用全局宏定义,可以使equalTo等效于mas_equalTo
  • #define MAS_SHORTHAND使用全局宏定义, 可以在调用masonry方法的时候不使用mas_前缀
 // 这里注意到一个地方,就是当使用了这个全局宏定义之后,发现可以有个类`NSArray+MASAdditions.h`,看了之后发现可以
self.buttonViews = @[ raiseButton, lowerButton, centerButton ];
// 之后可以在updateConstraints 方法中
- (void)updateConstraints {
[self.buttonViews updateConstraints:^(MASConstraintMaker *make) {
make.baseline.equalTo(self.mas_centerY).with.offset(self.offset);
}];
[super updateConstraints];
}
  • 动态修改视图约束:
 // 创建视图约束
[blueView mas_makeConstraints:^(MASConstraintMaker *make) {
self.animatableConstraint = make.edges.equalTo(superview).insets(paddingInsets).priorityLow();
]];
// 更改约束 (另一处方法中)
UIEdgeInsets paddingInsets = UIEdgeInsetsMake(padding, padding, padding, padding);
self.animatableConstraint.insets = paddingInsets;
[self layoutIfNeeded];
  • debug模式:
 // 对某个view添加key值
greenView.mas_key = @"greenView";
// 或者如下顺序
MASAttachKeys(greenView, redView, blueView, superview);
// 同样的对每条约束亦可以添加key
make.height.greaterThanOrEqualTo(@).key(@"ConstantConstraint");
  • preferredMaxLayoutWidth: 多行label的约束问题 iOS 6。
 // 已经确认好了位置
// 在layoutSubviews中确认label的preferredMaxLayoutWidth值
- (void)layoutSubviews {
[super layoutSubviews];
// 你必须在 [super layoutSubviews] 调用之后,longLabel的frame有值之后设置preferredMaxLayoutWidth
self.longLabel.preferredMaxLayoutWidth = self.frame.size.width-;
// 设置preferredLayoutWidth后,需要重新布局
[super layoutSubviews];
}
  • scrollView使用约束的问题:原理通过一个contentView来约束scrollView的contentSize大小,也就是说以子控件的约束条件,来控制父视图的大小。
 // 1. 控制scrollView大小(显示区域)
[self.scrollView makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(self.view);
}];
// 2. 添加一个contentView到scrollView,并且添加好约束条件
[contentView makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(self.scrollView);
// 注意到此处的宽度约束条件,这个宽度的约束条件是必添加项
make.width.equalTo(self.scrollView);
}];
// 3. 对contentView的子控件做好约束,达到可以控制contentView的大小
  • 新方法:2个或2个以上的控件等间隔排序。
 /**
* 多个控件固定间隔的等间隔排列,变化的是控件的长度或者宽度值
*
* @param axisType 轴线方向
* @param fixedSpacing 间隔大小
* @param leadSpacing 头部间隔
* @param tailSpacing 尾部间隔
*/
- (void)mas_distributeViewsAlongAxis:(MASAxisType)axisType
withFixedSpacing:(CGFloat)fixedSpacing l
eadSpacing:(CGFloat)leadSpacing
tailSpacing:(CGFloat)tailSpacing; /**
* 多个固定大小的控件的等间隔排列,变化的是间隔的空隙
*
* @param axisType 轴线方向
* @param fixedItemLength 每个控件的固定长度或者宽度值
* @param leadSpacing 头部间隔
* @param tailSpacing 尾部间隔
*/
- (void)mas_distributeViewsAlongAxis:(MASAxisType)axisType
withFixedItemLength:(CGFloat)fixedItemLength
leadSpacing:(CGFloat)leadSpacing
tailSpacing:(CGFloat)tailSpacing;
 //  使用方法很简单,因为它是NSArray的类扩展:

 //  创建水平排列图标 arr中放置了2个或连个以上的初始化后的控件
// alongAxis 轴线方向 固定间隔 头部间隔 尾部间隔
[arr mas_distributeViewsAlongAxis:MASAxisTypeHorizontal withFixedSpacing: leadSpacing: tailSpacing:];
[arr makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(@);
make.height.equalTo(@);
}];
  • 约束视图对象只有在被addSubview之后,才能给视图添加约束。
  • 当你的所有约束都在 updateConstraints 内调用的时候,你就需要在此调用此方法,因为 updateConstraints方法是需要触发的。
 // 调用在view 内部,而不是viewcontroller
+ (BOOL)requiresConstraintBasedLayout {
return YES;
} /**
* 苹果推荐 约束 增加和修改 放在此方法种
*/
- (void)updateConstraints {
[self.growingButton updateConstraints:^(MASConstraintMaker *make) {
make.center.equalTo(self);
make.width.equalTo(@(self.buttonSize.width)).priorityLow();
make.height.equalTo(@(self.buttonSize.height)).priorityLow();
make.width.lessThanOrEqualTo(self);
make.height.lessThanOrEqualTo(self);
}];
//最后记得回调super方法
[super updateConstraints];
}
  • 如果想要约束变换之后实现动画效果,则需要执行如下操作
 // 通知需要更新约束,但是不立即执行
[self setNeedsUpdateConstraints];
// 立即更新约束,以执行动态变换
// update constraints now so we can animate the change
[self updateConstraintsIfNeeded];
// 执行动画效果, 设置动画时间
[UIView animateWithDuration:0.4 animations:^{
[self layoutIfNeeded];
}];

参考链接:

Masonry介绍与使用实践(快速上手Autolayout)

Masonry使用心得

Masonry的简单使用

IOS自适应前段库-Masonry的使用

iOS_autoLayout_Masonry

Masonry自动布局详解一:基本用法Masonry自动布局详解二:动画更新约束

Masonry自动布局详解三:remake约束

Masonry自动布局详解四:整体动画更新约束

Masonry自动布局详解五:比例(multipliedBy)

Masonry自动布局详解六:tableviewCell布局

Masonry自动布局详解七:ScrollView循环布局

Masonry自动布局详解八:复杂ScrollView布局

Masonry自动布局详解九:scrollview实战场

Masonry使用注意篇