iOS常用从0到1学习iOS

iOS Masonry 约束相关

2021-08-12  本文已影响0人  一粒咸瓜子

Masonry 代码解析

控件自适应大小

scrollView

目标:让编译器自动推算 contentSize 的高度。
实现:将 contanierView 内部自上而下约束完整。

步骤:

UIScrollView *scrollView = [[UIScrollView alloc] init];
scrollView.backgroundColor = UIColor.blueColor;
scrollView.showsVerticalScrollIndicator = NO;
scrollView.showsHorizontalScrollIndicator = NO;
// scrollView 默认开启滚动效果
scrollView.alwaysBounceVertical = YES;
[self.view addSubview:scrollView];
[scrollView mas_makeConstraints:^(MASConstraintMaker *make) {
    make.edges.equalTo(self.view);
}];
UIView *containerView = [[UIView alloc] init];
[scrollView addSubview:containerView];
[containerView mas_makeConstraints:^(MASConstraintMaker *make) {
    make.edges.equalTo(scrollView);
    // 注意:要让编译器自动算,所以这里不能设置 height !!!
    make.width.equalTo(scrollView);
}];
_containerView = containerView;
// 设置 contentSize 可不设置。
// scrollView.contentSize = containerView.bounds.size;
// 1>> 内部约束自上而下,让编译器自己推算高度
[lastView mas_makeConstraints:^(MASConstraintMaker *make) {
    make.bottom.equalTo(containerView).offset(-20);
}];

// 或 2>>  给定 containerView 的高度
[containerView mas_makeConstraints:^(MASConstraintMaker *make) {
    make.height.mas_equalTo(300);
}];

View

目标:让编译器自动推算父控件高度。
实现:(跟 tableViewCell 高度自适应原理一样)外面的约束自上而下进行,如果编译器可以推算出最终高度,那么它就可以帮我们完成 父控件高度 的自适应工作。

UILabel *content_lb = [UILabel new];
[content_lb setText:@".........."];
content_lb.numberOfLines = 0;
[parentView addSubview:content_lb];
[content_lb mas_makeConstraints:^(MASConstraintMaker *make) {
    make.leading.mas_offset(5);
    make.trailing.mas_offset(-5);
    make.top.equalTo(head_iv.mas_bottom).offset(20);
    make.bottom.mas_equalTo(-21);
}];
   
[parentView mas_makeConstraints:^(MASConstraintMaker *make) {
    make.top.leading.trailing.equalTo(self.view);
}];

约束优先级 priority

约束的优先级:两个约束在某种情况下冲突,默认不执行其中优先级较低的约束。

同个属性,不同条件的约束的优先级

1> 需求:
产品名称:name 长度不定(富文本),如图1
产品详情:desc 长度不定(富文本),如图2
容量标签:capacity:

图1
图2
图3

2> 约束实现

[name_lb mas_makeConstraints:^(MASConstraintMaker *make) {
    make.top.equalTo(imageView);
    make.leading.equalTo(imageView.mas_trailing).offset(20 *MKScale);
    make.trailing.mas_offset(-16.5 *MKScale);
}];
   
[desc_lb mas_makeConstraints:^(MASConstraintMaker *make) {
    make.leading.trailing.equalTo(name_lb);
    make.top.equalTo(name_lb.mas_bottom).offset(5 *MKScale);
}];
[capacity_lb mas_makeConstraints:^(MASConstraintMaker *make) {
    make.leading.equalTo(name_lb);
    make.bottom.mas_equalTo(-20 *MKScale);
       
    make.top.greaterThanOrEqualTo(desc_lb.mas_bottom).offset(15 *MKScale);
    make.bottom.equalTo(product_iv).priority(500);
}];

3> 场景分析


关键点:

在iOS 11中,将 cell 布局好,不手动设置 rowHeight 和 restimatedRowHeight 也可以自动适应。(但在 iOS 10 中不可以)
tableView.rowHeight = UITableViewAutomaticDimension;
tableView.estimatedRowHeight = 200;

控件挤压、拉伸问题

场景:stackView 中有 redView 和 label 两个控件,手动设置 stackView 的宽度,必定会拉伸其中一个子控件。
如何控制拉伸哪个控件:
设置对应约束的优先级 priority,优先级低的会改变。

[imgView mas_makeConstraints:^(MASConstraintMaker *make) {
    make.width.mas_equalTo(30).priority(10); // imageView 优先级低,会被拉伸 或者直接设置 .priorityLow()
    make.height.mas_equalTo(20);
}];

[label mas_makeConstraints:^(MASConstraintMaker *make) {
    make.width.mas_equalTo(width).priority(100); // 或者直接设置 .priorityHigh() 要将约束写出来,不能采用自适应的方式
}];

动态更新约束

弱引用约束(推荐)

相当于重新创建约束(不是全部,重新创建的是想要更新的约束),适用各种情况,能立马更新。

@property (nonatomic, weak) MASConstraint *constraint;

[self.nameLabel mas_makeConstraints:^(MASConstraintMaker *make) {
    self.constraint = make.right.equalTo(self).priorityLow();
}];

- (void)changePriority:(BOOL)isHigh {
    // 让约束失效(内部调用uninstall,从约束组内移除,由于当前约束是弱引用,没有被其他指针强引用着则会被系统回收)
    [self.constraint uninstall];

    // 重新创建约束(mas_updateConstraints 会把 block 内的约束添加到约束组内并生效)
    [self.nameLabel mas_updateConstraints:^(MASConstraintMaker *make) {
        if (isHigh) {
            self.constraint = make.right.equalTo(self).priorityHigh();
        } else {
            self.constraint = make.right.equalTo(self).priorityLow();
        }
    }];
    // 刷新布局
    [self layoutIfNeeded];
}

强引用约束(oc不推荐,swift适用)

初始化后就立马修改的话(同一个Runloop循环内)并不会有变化,适用于初始化后晚一些再更新的情况。

oc(不推荐)

@property (nonatomic, strong) MASConstraint *constraint;

[self.nameLabel mas_makeConstraints:^(MASConstraintMaker *make) {
    self.constraint = make.right.equalTo(self).priorityLow();
}];

- (void)changePriority:(BOOL)isHigh {
    // 让约束失效(内部调用uninstall,从约束组内移除)
    [self.constraint deactivate]; 

    // 重新设置优先级
    if (isHigh) {
        self.constraint.priorityHigh();
    } else {
        self.constraint.priorityLow();
    }
    // 让约束生效(内部调用install,重新添加到约束组内)
    [self.constraint activate]; 

    // 刷新布局
    [self layoutIfNeeded];
}

swift(适用)

var bottomConstraint: Constraint?

view.snp.makeConstraints { (make) in
  // ...
  bottomConstraint = make.bottom.equalToSuperview().constraint  // 约束的引用
}
// 在某些条件下将其失效,重新设置约束
bottomConstraint?.deactivate()
bottomConstraint = nil

其他使用技巧

等间距约束

/**
 *  distribute with fixed spacing
 *
 *  @param axisType     which axis to distribute items along
 *  @param fixedSpacing the spacing between each item
 *  @param leadSpacing  the spacing before the first item and the container
 *  @param tailSpacing  the spacing after the last item and the container
 */
- (void)mas_distributeViewsAlongAxis:(MASAxisType)axisType withFixedSpacing:(CGFloat)fixedSpacing leadSpacing:(CGFloat)leadSpacing tailSpacing:(CGFloat)tailSpacing;
/**
 *  distribute with fixed item size
 *
 *  @param axisType        which axis to distribute items along
 *  @param fixedItemLength the fixed length of each item
 *  @param leadSpacing     the spacing before the first item and the container
 *  @param tailSpacing     the spacing after the last item and the container
 */
- (void)mas_distributeViewsAlongAxis:(MASAxisType)axisType withFixedItemLength:(CGFloat)fixedItemLength leadSpacing:(CGFloat)leadSpacing tailSpacing:(CGFloat)tailSpacing;
//Ex:
NSMutableArray <UIView *> *views = [NSMutableArray array];
for (int i = 0; i < info.count; i++) {
    UIView *view = [self createViewWithInfo:info[i]];
    [views addObject:view];
}
// 约束左右、间距
[views mas_distributeViewsAlongAxis:MASAxisTypeHorizontal withFixedSpacing:18 *MKScale leadSpacing:0 tailSpacing:0];
// 约束上下
[views mas_makeConstraints:^(MASConstraintMaker *make) {
    make.top.bottom.equalTo(self.pwdView);
}];

批量设置约束

假设有View1,view2,view3三个View,我们想要他们的宽高都等于CGSizeMake(100, 50)。我们可以对他们进行批量设置

NSValue *sizeValue = [NSValue valueWithCGSize:CGSizeMake(100, 50)];
[@[view1,view2,view3] mas_makeConstraints:^(MASConstraintMaker *make) {
    make.size.equalTo(sizeValue);
}];

由于我们还要设置view的top,left等位置约束。那可不可以在设置位置的mas_makeConstraints里面批量设置宽高呢?实际是可以的!

[view1 mas_makeConstraints:^(MASConstraintMaker *make) {
    (void)make.top.left;
    make.size.equalTo(@[view2,view3,sizeValue]);
}];

mas_key

当约束冲突发生的时候,我们经常为找不到是哪个View冲突的而烦恼,这时候我们可以设置View的key,就可以清晰的知道是哪个view了

view1.mas_key = @“view1”;
view2.mas_key = @“view2”;

// Masonry提供了批量设置的宏MASAttachKeys,只需要一句代码即可全部设置:
MASAttachKeys(view1,view2);

不使用 mas 前缀(不推荐)

加mas_前缀主要是在扩展系统类的时候为了避免与原有类冲突,这是Apple推荐的做法。不过目前来说,即使不加mas_前缀,也不会有什么问题。所以Masonry提供了不加mas_前缀的方法,只需要你定义几个宏即可。

1. MAS_SHORTHAND
定义MAS_SHORTHAND宏之后。可以使用UIView,NSArray中不带mas_前缀的makeConstraints,updateConstraints,remakeConstraints。以及UIView中不带mas_前缀的Attribute。
2. MAS_SHORTHAND_GLOBALS
默认的equalTo方法只接受id类型的对象。有时候我们想传入一个CGFloat, CGSize, UIEdgeInsets等。还需要将其转化成NSValue对象,比较麻烦。Masonry也考虑到了这种情况。只需要定义MAS_SHORTHAND_GLOBALS宏。就可以直接对equalTo传入基础类型。Masonry自动转化成NSValue对象

遇到的问题

UI校对与设计稿不一致

现象:虽然是按蓝湖标注来设置字体与间距的,但是效果缺不一致。
原因:蓝湖标注无法考虑字体内边距
解决办法:设置 label 高度为 字号+2 (+2的原因是如果字号与高度 1:1,某些字符会显示不全例如:g j),因为加了2个位置,会分摊在原先 label 的上下各1个像素点,所以要修改原先上下约束各-1。

xib 的控件取不到正确宽高

现象:有些时候,子控件需要依据父控件算宽高,如果是 xib 生成的控件(cell),代码取宽高取得是 xib 图形界面中显示的宽高



例如,将cell宽度拉至300,tableView 宽度 = 屏宽,此时在375 的屏下,显示 cell宽度为375,但是取值却还是300

解决办法:需要计算宽高的控件,其父视图用纯代码方式创建,尽量不使用 xib

上一篇 下一篇

猜你喜欢

热点阅读