Masonry使用以及源码解析(未完待续

news2024/11/25 12:59:00

文章目录

  • Masonry使用
    • 约束
    • 约束优先级 以及 intrinsicContentSize相关问题
  • Masonry:iOS12
  • Masonry源码解析
    • 下面是使用`make.width`点语法后的全部内部调用过程:

Masonry使用

约束

在写Masonry之前,我想先来聊聊约束的基础知识,我们首先要了解一个View的约束需要确定的是两个因素,一个是宽高信息,另外一个是位置信息。 只有确定这两个因素才能真正的确定一个View的约束,否则约束会爆警告。不管你怎么加约束,其实最后归根到底都是确实的这两个信息,那么我们了解这个有什么好处呢?我们可以通过约束转化来了解我们多添加了约束,是否缺失了某个约束,这种思想可以帮助我们快速查询问题所在。

但是有很多童鞋会发现在使用 Masonry 的时候,如果控件是UILabel,UIImageView,UIButton等这些组件及某些包含它们的系统组件只需要指定控件的位置约束,根本不需要指定宽高约束即可完成布局任务,这是为什么呢?这是因为这些控件中有 intrinsicContentSize 这个属性,intrinsicContentSize的作用其实很简单,它会自己根据内容计算出控件的固有宽高,在布局过程当你不指定宽高约束的时候,它就会生效。具体的内容我会在下面说到。这里就不过多叙述了。
首先,Masonry的添加布局主要有三个,三个方法的作用分别是创建约束;更新某个约束,其他约束不变;移除先前所有约束,添加新到的约束。这三个方法根据场景需要合理使用,否则可能造成内存问题,优化方式下面我们会来聊一下,这里就不过多叙述了。

- (NSArray *)mas_makeConstraints:(void(NS_NOESCAPE ^)(MASConstraintMaker *make))block;

- (NSArray *)mas_updateConstraints:(void(NS_NOESCAPE ^)(MASConstraintMaker *make))block;

- (NSArray *)mas_remakeConstraints:(void(NS_NOESCAPE ^)(MASConstraintMaker *make))block;

如果我们想设置一个具体的数值该怎么办呢?例如宽度我们想设置成10个单位,我们就可以如下设置。

[subView mas_makeConstraints:^(MASConstraintMaker *make) {
   make.width.equalTo(@10);
}];

我们发现上面代码还有个问题,那就是 equalTo 这个函数的参数必须是一个对象类型,这就很尴尬了,为啥,书写太麻烦,这时候我们可以使用 mas_equalTo 这个函数,示例如下所示。

[subView mas_makeConstraints:^(MASConstraintMaker *make) {
   make.width.mas_equalTo(10);
}];

上面书写的好像也不是一个最优的方案,虽然我们解决了后面的问题,但是前面的代码字母数又多了,这时候我们可以在我们的文件之前加上一个 #define MAS_SHORTHAND_GLOBALS 这样的宏定义,就可以直接使用
equalTo(10)了,如下所示。

使用了 #ifdef 条件编译指令来检查宏 MAS_SHORTHAND_GLOBALS 是否已经被定义。如果该宏已经被定义,那么代码块中的宏定义就会生效;否则,这些宏定义将会被忽略。

如果 MAS_SHORTHAND_GLOBALS 被定义,那么这些宏定义将会用来简化代码的书写,使得使用 Masonry 更加方便。例如,equalTo 会被定义为 mas_equalTo,greaterThanOrEqualTo 会被定义为 mas_greaterThanOrEqualTo,lessThanOrEqualTo 会被定义为 mas_lessThanOrEqualTo,offset 会被定义为 mas_offset。这些缩写的函数和宏会将参数转发到对应的 Masonry 函数或宏中,从而实现布局计算

#define MAS_SHORTHAND_GLOBALS

[subView mas_makeConstraints:^(MASConstraintMaker *make) {
   make.width.equalTo(10);
}];

如果我们想要设置subView的宽度等于父视图的宽度的50%,这时候我们该怎么编写我们的约束呢?我们可以用到 multipliedBydividedBy这两个方法,一个是乘法,一个是除法,较为简单,示例代码如下所示。

//乘法
[subView mas_makeConstraints:^(MASConstraintMaker *make) {
   make.width.equalTo(self).multipliedBy(0.5);
}];
//除法
[subView mas_makeConstraints:^(MASConstraintMaker *make) {
   make.width.equalTo(self).dividedBy(2);
}];

如果我们想要subView的宽度等于高度的2倍,这时候该怎么办呢?我们需要指定equalTo()里面具体的值(实际上是传一个 MASViewAttribute 对象),而不是简单的传一个控件对象,示例代码如下所示。

[subView mas_makeConstraints:^(MASConstraintMaker *make) {
   make.width.equalTo(subView.mas_height).multipliedBy(2);
}];

我们就简单叙述一下约束优先级设置,Masonry为我们提供了三个优先级的方法,priorityLow()、priorityMedium()、priorityHigh(),这三个方法内部对应着不同的默认优先级,当然我们也可以使用priority() 设置具体的数值。示例代码如下所示,关于约束优先级具体使用也会在后面的模块中说到。

[subView mas_makeConstraints:^(MASConstraintMaker *make) {
   make.width.equalTo(subView4).priorityLow();
   make.width.equalTo(@[subView2, subView3,@100]).priorityHigh();
   make.width.equalTo(@300).priority(888);
}];

如果我们想让subView的宽度是 父视图的宽度的30% + 10个单位长度,这时候我们该怎么设置呢?其实这时候有点类似于 CSS 中的 calc() 函数,我们肯定不能设置两条约束条件,如果那样设置了,后面的约束条件就会把前面的约束条件给覆盖掉,对此我们如下设置即可。(offset方法和multipliedBy方法顺序无影响)

[subView mas_makeConstraints:^(MASConstraintMaker *make) {
  make.width.equalTo(self).offset(10).multipliedBy(0.3);
}];

约束优先级 以及 intrinsicContentSize相关问题

约束优先级以及intrinsicContentSize的相关问题是我们不得不提到的问题.

首先来说一下为什么要有约束优先级,我们给定一个场景,假设我们设置在一个superView(宽度为 200)中的一个View子视图的左右边距都为0,然后第二个约束是视图的宽度为100,这时候就会出现问题,因为如果左右边距都为0,那么视图宽度为200,这样和第二个约束条件就发生了冲突,系统是不允许这样的问题出现的.那么我们想不在删除约束的情况下,该如何解决这种问题呢?这时候我们就需要通过设置约束优先级来解决这一类问题,系统通过比较两个”相互冲突的约束”的优先级,从而忽略低优先级的某个约束,达到正确布局的目的约束优先级默认都是1000.所以我们给设定一个根据具体情况设置一个合适的值即可,代码如下所示.

[subView mas_makeConstraints:^(MASConstraintMaker *make) {
   make.left.right.equalTo(self);
   make.width.equalTo(@100).priority(888);
}];

约束优先级主要是应对与单个视图中多个约束发生冲突的时候解决问题的方案.而 intrinsicContentSize 主要应对于多个视图约束发生冲突的解决方案,我们就对着具体的实例来进行分析.

在最前面我们说到 在AutoLayout中, intrinsicContentSize的作用其实很简单,它会自己根据内容计算出控件的固有宽高,在布局过程当你不指定宽高约束的时候,它就会生效。

这个属性是非常的好用,但是也会出现对应的问题.例如我们现在有两个Label,两个Lable的约束条件如下所示.

[label1 mas_makeConstraints:^(MASConstraintMaker *make) {
   make.left.equalTo(superView);
   make.top.equalTo(superView);
}];

[label2 mas_makeConstraints:^(MASConstraintMaker *make) {
   make.left.equalTo(label1.mas_right);
   make.top.equalTo(superView);
}];

上面的情况完全没有任何的问题,因为 intrinsicContentSize 属性的原因,我们轻松完成布局任务,但是当我们给 label2 添加一个 右边距等于superView.代码如下所示.

[label2 mas_makeConstraints:^(MASConstraintMaker *make) {
   make.left.equalTo(label1.mas_right);
   make.right.equalTo(superView);
   make.top.equalTo(superView);
}];

这时候就会出现问题,label1 和 label2 必然有一个不能满足 intrinsicContentSize 约束条件,必然有一个需要拉伸才能完成约束布局任务,我们称这种问题叫做 Intrinsic冲突.

解决 Intrinsic冲突 一共有两种方案,一种是直接指定冲突的label 1 和 label 2的宽高约束信息.第二种就是利用 content Hugging/content Compression Resistance.原始方法如下所示.

- (void)setContentHuggingPriority:(UILayoutPriority)priority forAxis:(UILayoutConstraintAxis)axis API_AVAILABLE(ios(6.0));
- (void)setContentCompressionResistancePriority:(UILayoutPriority)priority forAxis:(UILayoutConstraintAxis)axis API_AVAILABLE(ios(6.0));

Content Hugging 约束(不想变大约束)表示:如果组件的此属性优先级比另一个组件此属性优先级高的话,那么这个组件就保持不变,另一个可以在需要拉伸的时候拉伸。属性分横向和纵向2个方向。

Content Compression Resistance 约束(不想变小约束)表示:如果组件的此属性优先级比另一个组件此属性优先级高的话,那么这个组件就保持不变,另一个可以在需要压缩的时候压缩。属性分横向和纵向2个方向。 意思很明显。上面UIlabel这个例子中,很显然,如果某个UILabel使用Intrinsic Content Size的时候,另一个需要拉伸。 所以我们需要调整两个UILabel的 Content Hugging约束的优先级就可以啦。
所以我们可以通过设置这两个方法来解决 Intrinsic冲突 问题,假设 我们想让 label 2 拉伸,label1尽量不拉伸,我们就可以设置如下代码.具体代码如下所示.

[label1  setContentHuggingPriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisHorizontal];
[label2  setContentHuggingPriority:UILayoutPriorityDefaultLow forAxis:UILayoutConstraintAxisHorizontal];

Masonry:iOS12

我们都知道,其实Masonry是封装系统的NSLayoutConstraints,简化了代码,但是在iOS12之前NSLayoutConstraints存在着致命的问题,那就是性能问题,其实这个在iOS12之后也会存在只是小了很多。那么在iOS12之前到底是什么原因导致这些问题呢?接下来我们逐一分析各种情况。

AutoLayout使用的布局算法其实是 Cassowary,在WWDC2018,官方对其性能问题提出了说明,如下图所示,我们可以清楚的看到iOS12前的AutoLayout布局性能是成指数性增长的。
![[Pasted image 20230507163808.png]]
但是不是所有的布局都有这样的问题呢?答案当然是否定的,如下图所示。所以说AutoLayout只是在某些情况存在着问题。
![[Pasted image 20230507163843.png]]
那么真正的原始是什么呢?因为iOS12之前,当有约束变化时都会重新创建一个计算引擎 NSISEngier 将约束关系重新加起来,重新计算。涉及到约束关系变多时,新的计算引擎需要重新计算,最终导致计算量指数级增加。

iOS12的AutoLayout更多的利用了Cassowary算法的界面更新策略,使其真正完成了高效的界面线性策略计算。使其尽量成线程增加,减少性能问题,最后允许我唠叨一句,讲真的,性能再强也是干不过Frame布局方式的,但是胜在简单方便。

Masonry源码解析

在这里插入图片描述

Masonry 主要方法由上述例子就可一窥全貌。Masonry主要通过对 UIViewNSView)、NSArrayUIViewController 进行分类扩展,从而提供自动布局的构建方法。
对于一个一般的布局约束,研究一下约束方法

   [_masParView mas_makeConstraints:^(MASConstraintMaker *make) {

        make.edges.equalTo(self.view).with.insets(UIEdgeInsetsMake(100, 100, 100, 100));

    }];

调用的方法是UIView的分类MASAdditions中的mas_makeConstraints:方法

/**
 *  Creates a MASConstraintMaker with the callee view.
 *  Any constraints defined are added to the view or the appropriate superview once the block has finished executing
 *  **@param** block scope within which you can build up the constraints which you wish to apply to the view.
 *  **@return** Array of created MASConstraints
 *  使用被调用的视图创建一个MASConstraintMaker。一旦代码块执行完毕,任何定义的约束
 *  都会被添加到视图或适当的父视图中
 *  @参数 块作用域,您可以在其中构建您希望应用于视图的约束。
 *  @返回 创建的MASConstraints的数组
 */

- (NSArray *)mas_makeConstraints:(**void**(NS_NOESCAPE ^)(MASConstraintMaker *make))block;

实现

- (NSArray *)mas_makeConstraints:(void(^)(MASConstraintMaker *))block {

    self.translatesAutoresizingMaskIntoConstraints = NO;

    MASConstraintMaker *constraintMaker = [[MASConstraintMaker alloc] initWithView:self];

    block(constraintMaker);

    return [constraintMaker install];

}

该方法会创建一个 MASConstraintMaker 对象,并将当前视图作为其初始化参数。然后,该方法将传入的 block 块作为参数传递给 MASConstraintMaker 对象,并调用其中的方法来描述视图的约束。最后,该方法调用 install 方法,将约束应用到当前视图上,并返回一个包含所有约束的数组。

首先,该方法将 translatesAutoresizingMaskIntoConstraints 属性设置为 NO,这是为了确保使用自动布局约束而非默认的 frame 布局方式。

然后,该方法创建了一个 MASConstraintMaker 对象,并将当前视图作为其初始化参数。接着,该方法将传入的 block 块作为参数传递给 MASConstraintMaker 对象,从而描述了视图之间的约束关系。这个 block 块参数中的 MASConstraintMaker 对象包含了各种用于描述视图约束的方法。在调用 block 块时,我们可以使用这些方法来描述当前视图与其它视图之间的相对位置、尺寸等信息。

最后,该方法调用 install 方法,将约束应用到当前视图上,并返回一个包含所有约束的数组。install 方法会根据之前设置的约束信息计算出合适的约束,并将其添加到相应的视图上。在这个方法返回之后,当前视图就会按照之前设置的约束进行自动布局。

- (id)initWithView:(MAS_VIEW *)view {
    self = [super init];
    if (!self) return nil;
    
    self.view = view;
    self.constraints = NSMutableArray.new;
    
    return self;
}

该方法用于创建一个 MASConstraintMaker 对象,该对象包含了用于描述视图约束的方法。在使用 Masonry 自动布局库时,我们通常需要先创建一个 MASConstraintMaker 对象,然后使用其中的方法描述视图之间的约束关系,最后调用 install 方法将约束应用到相应的视图上。

在这个方法中,我们首先调用父类 NSObjectinit 方法进行初始化,如果初始化失败,则直接返回 nil。接着,我们将传入的 view 参数保存到 self.view 属性中。这个 view 参数指的是我们需要设置约束的视图对象。

我们还创建了一个 NSMutableArray 类型的数组,用于保存后续添加的约束。我们将这个数组保存到 self.constraints 属性中。

最后,该方法返回初始化后的 MASConstraintMaker 对象。这个对象包含了各种用于描述视图约束的方法,我们可以在之后的代码中使用这些方法来描述视图之间的约束关系。

mas_makeConstraints 方法中传递的块参数是一个 block,这个 block 的参数类型是 MASConstraintMaker 对象,即一个用于描述视图约束的对象。在 block 中,我们可以使用 MASConstraintMaker 对象提供的各种方法描述视图之间的约束关系。

具体来说,当我们调用 mas_makeConstraints 方法时,Masonry 会为我们创建一个 MASConstraintMaker 对象,并将这个对象作为 block 的参数传递给我们。我们可以在 block 中使用这个 MASConstraintMaker 对象的各种方法描述视图之间的约束关系,例如:

[view1 mas_makeConstraints:^(MASConstraintMaker *make) {     
make.left.top.equalTo(superview).offset(10);     
make.right.bottom.equalTo(superview).offset(-10); 
}];

在这个示例中,我们使用了 make.left.top.equalTo(superview).offset(10)make.right.bottom.equalTo(superview).offset(-10) 这两个方法来描述视图之间的约束关系。这些方法的作用是将 view1 的左侧和顶部与 superview 的左侧和顶部对齐,并向右和向下偏移 10 个单位,同时将 view1 的右侧和底部与 superview 的右侧和底部对齐,并向左和向上偏移 10 个单位。

在 block 中,我们可以使用 MASConstraintMaker 对象的各种方法来描述视图之间的约束关系,例如设置视图的位置、大小、间距等。当 block 执行完毕后,Masonry 会根据我们在 block 中描述的约束关系来自动计算视图的位置和大小,并将这些约束关系应用到相应的视图上,从而实现自动布局。

下面是使用make.width点语法后的全部内部调用过程:

// MASConstraintMaker
- (MASConstraint *)width {
    return [self addConstraintWithLayoutAttribute:NSLayoutAttributeWidth];
}

- (MASConstraint *)addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {
    return [self constraint:nil addConstraintWithLayoutAttribute:layoutAttribute];
}

- (MASConstraint *)constraint:(MASConstraint *)constraint addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {
    // 根据 约束属性 和 视图 创建一个约束单元
    MASViewAttribute *viewAttribute = [[MASViewAttribute alloc] initWithView:self.view layoutAttribute:layoutAttribute];
    //创建约束,以约束单元作为约束的第一项
    MASViewConstraint *newConstraint = [[MASViewConstraint alloc] initWithFirstViewAttribute:viewAttribute];
    if ([constraint isKindOfClass:MASViewConstraint.class]) {
        //replace with composite constraint
        // 如果是在已有约束的基础上再创建的约束,则将它们转换成一个 组合约束,并将原约束替换成该组合约束。
        NSArray *children = @[constraint, newConstraint];
        MASCompositeConstraint *compositeConstraint = [[MASCompositeConstraint alloc] initWithChildren:children];
        compositeConstraint.delegate = self;
        // 这里会将原来 make.width 添加的约束 替换成一个 组合约束(宽度约束 + 高度约束)
        [self constraint:constraint shouldBeReplacedWithConstraint:compositeConstraint];
        // 返回组合约束
        return compositeConstraint;
    }
    if (!constraint) {// 如果不是在已有约束的基础上再创建约束,则添加约束至列表
        newConstraint.delegate = self;// 注意这一步,会对 make.top.left 这种情形产生关键影响
        [self.constraints addObject:newConstraint];
    }
    return newConstraint;
}

在第二次设置约束时(.height)会进入不同的流程。注意上面提到的newConstraint.delegate设置代理:

//MAConstraint
- (MASConstraint *)height {
    return [self addConstraintWithLayoutAttribute:NSLayoutAttributeHeight];
}
//MSViewConstraint
- (MASConstraint *)addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {
    NSAssert(!self.hasLayoutRelation, @"Attributes should be chained before defining the constraint relation");
	//delegate是MASConstraintMaker
    return [self.delegate constraint:self addConstraintWithLayoutAttribute:layoutAttribute];
}
// MASConstraintMaker
- (MASConstraint *)constraint:(MASConstraint *)constraint addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {...}

下面看一下.mas_equalTo(@100)的流程。

// MASConstraint
#define mas_equalTo(...)                 equalTo(MASBoxValue((__VA_ARGS__)))
- (MASConstraint * (^)(id))equalTo {
    return ^id(id attribute) {
        // attribute 可能是 @100 类似的值,也可能是 view.mas_width等这样的
        return self.equalToWithRelation(attribute, NSLayoutRelationEqual);
    };
}
- (MASConstraint * (^)(id))mas_equalTo {
    return ^id(id attribute) {
        return self.equalToWithRelation(attribute, NSLayoutRelationEqual);
    };
}
// MASViewConstraint
- (MASConstraint * (^)(id, NSLayoutRelation))equalToWithRelation {
    return ^id(id attribute, NSLayoutRelation relation) {
        if ([attribute isKindOfClass:NSArray.class]) {//是数组(有多个约束)
            NSAssert(!self.hasLayoutRelation, @"Redefinition of constraint relation");
            NSMutableArray *children = NSMutableArray.new;
            for (id attr in attribute) {
                MASViewConstraint *viewConstraint = [self copy];
                viewConstraint.layoutRelation = relation;
                viewConstraint.secondViewAttribute = attr;// 设置约束第二项
                [children addObject:viewConstraint];
            }
            MASCompositeConstraint *compositeConstraint = [[MASCompositeConstraint alloc] initWithChildren:children];
            compositeConstraint.delegate = self.delegate;
            [self.delegate constraint:self shouldBeReplacedWithConstraint:compositeConstraint];
            return compositeConstraint;
        } else {//单个约束
            NSAssert(!self.hasLayoutRelation || self.layoutRelation == relation && [attribute isKindOfClass:NSValue.class], @"Redefinition of constraint relation");
            self.layoutRelation = relation;
            self.secondViewAttribute = attribute;// 设置约束第二项
            return self;
        }
    };
}
// 设置约束第二项
- (void)setSecondViewAttribute:(id)secondViewAttribute {
	//判断类型
    if ([secondViewAttribute isKindOfClass:NSValue.class]) {
        [self setLayoutConstantWithValue:secondViewAttribute];
    } else if ([secondViewAttribute isKindOfClass:MAS_VIEW.class]) {
        _secondViewAttribute = [[MASViewAttribute alloc] initWithView:secondViewAttribute layoutAttribute:self.firstViewAttribute.layoutAttribute];
    } else if ([secondViewAttribute isKindOfClass:MASViewAttribute.class]) {
        _secondViewAttribute = secondViewAttribute;
    } else {
        NSAssert(NO, @"attempting to add unsupported attribute: %@", secondViewAttribute);
    }
}
// MASConstraint
- (void)setLayoutConstantWithValue:(NSValue *)value {
    if ([value isKindOfClass:NSNumber.class]) {
        self.offset = [(NSNumber *)value doubleValue];
    } else if (strcmp(value.objCType, @encode(CGPoint)) == 0) {
        CGPoint point;
        [value getValue:&point];
        self.centerOffset = point;
    } else if (strcmp(value.objCType, @encode(CGSize)) == 0) {
        CGSize size;
        [value getValue:&size];
        self.sizeOffset = size;
    } else if (strcmp(value.objCType, @encode(MASEdgeInsets)) == 0) {
        MASEdgeInsets insets;
        [value getValue:&insets];
        self.insets = insets;
    } else {
        NSAssert(NO, @"attempting to set layout constant with unsupported value: %@", value);
    }
}

// MASViewConstraint
- (void)setOffset:(CGFloat)offset {
    self.layoutConstant = offset;       // 设置约束常量
}
- (void)setSizeOffset:(CGSize)sizeOffset {
    NSLayoutAttribute layoutAttribute = self.firstViewAttribute.layoutAttribute;
    switch (layoutAttribute) {
        case NSLayoutAttributeWidth:
            self.layoutConstant = sizeOffset.width;
            break;
        case NSLayoutAttributeHeight:
            self.layoutConstant = sizeOffset.height;
            break;
        default:
            break;
    }
}
- (void)setCenterOffset:(CGPoint)centerOffset {
    NSLayoutAttribute layoutAttribute = self.firstViewAttribute.layoutAttribute;
    switch (layoutAttribute) {
        case NSLayoutAttributeCenterX:
            self.layoutConstant = centerOffset.x;
            break;
        case NSLayoutAttributeCenterY:
            self.layoutConstant = centerOffset.y;
            break;
        default:
            break;
    }
}
- (void)setInsets:(MASEdgeInsets)insets {
    NSLayoutAttribute layoutAttribute = self.firstViewAttribute.layoutAttribute;
    switch (layoutAttribute) {
        case NSLayoutAttributeLeft:
        case NSLayoutAttributeLeading:
            self.layoutConstant = insets.left;
            break;
        case NSLayoutAttributeTop:
            self.layoutConstant = insets.top;
            break;
        case NSLayoutAttributeBottom:
            self.layoutConstant = -insets.bottom;
            break;
        case NSLayoutAttributeRight:
        case NSLayoutAttributeTrailing:
            self.layoutConstant = -insets.right;
            break;
        default:
            break;
    }
}

另外,后面的 offset 方法做了一步额外的操作:

// MASConstraint
- (MASConstraint * (^)(CGFloat))offset {
    return ^id(CGFloat offset){
        self.offset = offset;
        return self;
    };
}
- (void)setOffset:(CGFloat)offset {
    self.layoutConstant = offset;
}

- (NSArray *)mas_makeConstraints:(void(^)(MASConstraintMaker *make))block {

    NSMutableArray *constraints = [NSMutableArray array];

    for (MAS_VIEW *view in self) {

        NSAssert([view isKindOfClass:[MAS_VIEW class]], @"All objects in the array must be views");

        [constraints addObjectsFromArray:[view mas_makeConstraints:block]];

    }

    return constraints;

}

该方法用于为一个包含多个视图的数组设置约束。具体地,这个方法会遍历数组中的每个视图,并对每个视图调用其自身的 mas_makeConstraints 方法,将传入的 block 块作为参数传递给它。

block 块参数中的 MASConstraintMaker 对象包含了各种用于描述视图约束的方法。在调用 mas_makeConstraints 方法时,传入的 block 块中可以使用这些方法来描述视图之间的相对位置、尺寸等信息。mas_makeConstraints 方法会根据这些信息计算出合适的约束,并将其添加到相应的视图上。

在该方法中,为了遍历数组中的每个视图并添加约束,我们使用了 for-in 循环。对于每个视图,我们首先使用 NSAssert 函数来断言其是否属于 MAS_VIEW 类型,如果不是则会抛出异常,以保证该数组只包含视图对象。接着,我们将视图的 mas_makeConstraints 方法返回的约束添加到 constraints 数组中。最后,该方法返回 constraints 数组,其中包含了为数组中的每个视图设置的约束。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/499352.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

igraph的layout布局

做图论的社区检测,需要画图显示,用igraph可以进行可视化。 igraph有几个布局,分别如下: layout_with_dh : The Davidson-Harel layout algorithm Place vertices of a graph on the plane, according to the simulat…

电脑远程控制

这里介绍2种常用的电脑远程控制方式。第一种,向日葵。第二种,QQ。 向日葵 --- 推荐 支持 Mac、Windows、Linux、iOS、Android。 Mac 版向日葵如果想让别人控制你的电脑,安装完向日葵以后要在 Mac 的“安全与隐私”开启几个权限才能被对方正…

node笔记_写文件(异步写入,同步写入,追加写入,流式写入)

文章目录 ⭐前言⭐写文件💖writeFile写入写一个txt文件 💖writeFileSync同步写入写一个txt文件 💖appendFile 追加写入追加写入一个txt 💖createWriteStream流式写入流式写入txt ⭐ 结束 ⭐前言 大家好,我是yma16&…

【计算机网络】 总结复习(2)

tcp tcp 工作在传输层可靠的数据传输服务,确保传输数据是无损坏,无间隔,非冗余按序 一些知识点 服务端最大并发 TCP 连接数远不能达到理论上限,会受以下因素影响: 文件描述符限制,每个 TCP 连接都是一个文…

HTML-CSS学习笔记

day1-01.CSS的元素显示模式 元素的显示模式就是元素&#xff08;标签&#xff09;以什么方式进行展示&#xff0c;比如<div>自己占一行&#xff0c;<span>一行可以放多个。 HTML元素一般分为块元素和行内元素两种类型。 块元素 如果在p标签中放了div标签&#xff…

操作系统——存储管理方式

目录 1.分区存储管理 1-1单一连续分配 1-1-1基本原理&#xff1a; 1-1-2单一连续分区存储管理的地址变换与地址保护 1-1-3管理特点 1-2固定分区分配 1-2-1基本原理 1-2-2分区划分 1-2-3主存空间的分配与回收 1-2-4地址转换与存储保护 1-2-5管理特点 1-3动态分区分配…

软考信管高级——风险管理

风险管理内容 风险管理计划 包含的内容&#xff1a; (1)方法论 (2)角色与职责 (3)预算 (4)时间安排 (5)风险类别 (6)风险概率和影响的定义 (7)概率和影响矩阵 (8)修订的干系人承受力 (9)报告格式 (10)跟踪 风险类型和应对措施 可能遇到的风险&#xff1a; (1)需求风险;(2)技术…

CSS布局基础(精灵图 字体图标 css 三角图标)

精灵图 & 字体图标 精灵图使用字体图标下载字体图标使用方式icomoon阿里 iconfontttf 字体 unicodecss 方式js 方式 更新字体图标icomoon阿里 iconfont css三角图标标准三角&#xff08;垂直的两边相等&#xff09;先来个普通盒子&#xff08;当然是五彩斑斓的边&#xff…

《编程思维与实践》1052.删除注释

《编程思维与实践》1052.删除注释 题目 思路 将所有可能的情况枚举出来: 1.在有效的块注释内: 有效是指块注释不在引号内,如char *s" \ * xxxxxxx *\ "就不是一个有效的块注释, 这种情况下跳过之后所有的内容,直到遇到*/后才重新判断情况; 2.在有效的行注释内: 同理…

ChatGPT :国内免费可用 ChatGPT +Midjourney绘图

前言 ChatGPT&#xff08;全名&#xff1a;Chat Generative Pre-trained Transformer&#xff09;&#xff0c;美国OpenAI 研发的聊天机器人程序 &#xff0c;于2022年11月30日发布 。ChatGPT是人工智能技术驱动的自然语言处理工具&#xff0c;它能够通过理解和学习人类的语言来…

Spring Security 06 Rember Me

目录 基本使用 原理分析 RememberMeServices TokenBasedRememberMeServices 总结 内存令牌 PersistentTokenBasedRememberMeServices 使用内存中令牌实现 持久化令牌 自定义记住我 自定义认证类 LoginFilter 自定义 RememberMeService 配置记住我 RememberMe …

使用多线程执行任务,并获取返回结果,附异步实现

1 获取又返回结果的 需要用到 callable接口 public class TestTask implements Callable<Student> {Overridepublic Student call() throws Exception {Thread.sleep(1500);Student student new Student();student.setAge(10);student.setName("里里");Syste…

Ceph对象存储使用

文章目录 对象存储简介RadosGW简介RadosGW配置RGW使用的存储池配置rgw使用的http端口配置rgw使用https配置rgw高可用 客户端s3cmd测试数据读写创建rgw用户安装s3cmd客户端配置s3cmd访问rgw测试数据读写bucket授权 对象存储简介 对象存储是无层次结构的数据存储方法&#xff0c…

QT+OpenGL反射与折射

文章目录 QTOpenGL反射与折射反射折射 QTOpenGL反射与折射 本篇完整工程见gitee:QtOpenGL 对应点的tag&#xff0c;由turbolove提供技术支持&#xff0c;您可以关注博主或者私信博主 反射 反射这个属性表现为物体(或者物体的一部分)反射它周围的环境&#xff0c;即根据观察者…

【Python入门篇】——Python基础语法(字符串格式化,表达式格式化和数据输入)

作者简介&#xff1a; 辭七七&#xff0c;目前大一&#xff0c;正在学习C/C&#xff0c;Java&#xff0c;Python等 作者主页&#xff1a; 七七的个人主页 文章收录专栏&#xff1a; Python入门&#xff0c;本专栏主要内容为Python的基础语法&#xff0c;Python中的选择循环语句…

STM32CubeMx+HAL库+小熊派+FreeRTOS+EasyLogger+Gitee+手把手教你

文章目录 1、创建工程配置RCC与SYS配置LED配置KEY配置串口生成工程 2、手动移植Freertos获取源码移植include移植portable移植src复制并修改FreeRTOSConfig.hkeil中添加路径与配置 3、移植EasyLogger获取源码复制easylogger文件keil中添加路径修改elog.h修改elog_port.c修改elo…

MacOS自定义安装 Python

Python 下载地址 官网下载太慢&#xff0c;如下是国内的镜像源&#xff0c;各版本都有&#xff1a;Python 国内镜像 下载好后缀是 tgz 的包进行解压 tar -zxvf Python-3.9.16.tgz进入目录并且对进行配置&#xff0c;编译(根据自己的目录进行调整) ./configure --with-opens…

Excel技能之图表,会Excel就能碾压程序员

数据可视化&#xff0c;让数据说话&#xff0c;需要Excel图表的呈现。你越用心&#xff0c;画的图越好看。 同一份数据&#xff0c;用不同的Excel图表展现出来&#xff0c;效果各有千秋。使用正确的图表&#xff0c;从一堆杂乱无章的数据中找出规律。不仅要知道怎么用图表&…

sentinel 随笔 1-流控

0. 想要个半个月的旅游 最近发现算法比较有意思一些&#xff0c;什么企业框架都是看不完的… 书接 FlowSlot 1. FlowRuleChecker.checkFlow() : 配置的规则校验类 sentinel 并没有对这个Checker进行抽象的设计&#xff0c;第一次看有些别扭… package com.alibaba.csp.sent…

矢量绘图UI设计Sketch

Sketch是一款Mac操作系统上常用的矢量图形编辑软件&#xff0c;旨在帮助用户设计和创建高质量的UI和UX界面。 软件安装&#xff1a;Sketch 中文 以下是Sketch软件的一些主要特点&#xff1a; 矢量工具和对象&#xff1a;Sketch提供了多种画线、填充、阴影、文本和形状等矢量工…