masonry_masonry报错 - CSDN
精华内容
参与话题
  • Masonry 框架小结

    2019-06-10 10:27:27
    Masonry 框架小结 自从 iOS 6.0 苹果引入自动布局以来,经过不断的发展,其已经成为我们进行 UI 布局的主要技术之一。在 Autolayout 之前,可以使用 Autoresizing 来约束视图的父子关系,但是局限性很大,很多任务...

    Masonry 框架小结

    自从 iOS 6.0 苹果引入自动布局以来,经过不断的发展,其已经成为我们进行 UI 布局的主要技术之一。在 Autolayout 之前,可以使用 Autoresizing 来约束视图的父子关系,但是局限性很大,很多任务无法实现,如其不能约束兄弟视图之间的关系。

    相比之下,Autolayout 的功能则强大的多,其核心概念便是参照约束

    NSLayoutConstraint

    自动布局的关键是创造完整的约束,并将约束添加到正确的视图中。

    NSLayoutConstraint 类是对约束的描述,其核心概念可以用一个公式表示,如下:

    firstItem.firstAttribute {==,<=,>=} secondItem.secondAttribute * multiplier + constant

    对于视图自身大小尺寸的约束而言,是不需要 secondItem 参照物的。不过,如果将此情况视为以自身为参照,那么就可以将约束理解为总是参照其他视图而存在的,即约束是相对而言的。

    该类提供了两个类方法用来创建约束实例:

    +(instancetype)constraintWithItem:(id)view1 attribute:(NSLayoutAttribute)attr1 relatedBy:(NSLayoutRelation)relation toItem:(nullable id)view2 attribute:(NSLayoutAttribute)attr2 multiplier:(CGFloat)multiplier constant:(CGFloat)c;
    
    + (NSArray<__kindof NSLayoutConstraint *> *)constraintsWithVisualFormat:(NSString *)format options:(NSLayoutFormatOptions)opts metrics:(nullable NSDictionary<NSString *,id> *)metrics views:(NSDictionary<NSString *, id> *)views;
    

    第一个方法,基本上就是对核心公式的赋值。而第二个方法,则是使用 VFL(Visual Format Language) 字符串来创建约束。

    要准确快速的使用约束,需要了解 NSLayoutAttributeNSLayoutRelationUILayoutPriority 的可选值。

    对于 NSLayoutAttribute 而言,需要注意的是 NSLayoutAttributeLeftNSLayoutAttributeRight 总是表示视图的左、右两侧,但是 NSLayoutAttributeLeadingNSLayoutAttributeTrailing 却不总是表示视图的左、右两侧,其表示的含义同当地视图排版习惯相关。所以,其表示的含义可能是左、右两侧,也可能是右、左两侧。

    NSLayoutAttribute***Margin 表示的参照位置总是同 UIViewlayoutMargins 属性相关。

    创建的约束要添加到视图中才会生效,但是需要将 UIViewtranslatesAutoresizingMaskIntoConstraints 属性置为 NO,并且约束相关联的视图必须都添加到视图层级中,而且约束需要添加到相关视图的共同父视图中(将视图本身看作其本身的父视图,即对于有包含关系的相关视图,约束要添加到层级高的视图中,而只涉及自身的约束,则添加到其自身即可)。

    约束的使用的确减少了视图的布局和适配工作,但是直接使用 NSLayoutConstraint 类定义约束是十分繁琐的,并且代码会变得十分臃肿,不易理解、查错和维护,所以第三方框架的使用必不可少。

    Masonry

    Masonry 框架十分易用,支持链式编程,其实现并不复杂,但是设计的十分巧妙。理解 Masonry 中约束的实现过程,可以从约束的核心公式入手。

    MASViewAttribute

    MASViewAttribute 类,可以理解为对 NSLayoutAttribute 的封装,其除了保存布局时要参照的位置外,还持有了具体的布局对象和参照对象。

    @property (nonatomic, assign, readonly) NSLayoutAttribute layoutAttribute;
    
    @property (nonatomic, weak, readonly) MAS_VIEW *view;
    @property (nonatomic, weak, readonly) id item;
    

    可见,布局对象是 UIView 对象,而参照对象则未必。并且,两者都是弱引用。

    MASLayoutConstraint

    MASLayoutConstraint 只是简单继承了 NSLayoutConstraint 类,可以预见,其是该框架中描述约束的类。另外,其定义了一个属性以便于调试,通过该属性,可以得知一个约束是否是通过该框架创建的。

    @property (nonatomic, strong) id mas_key;
    

    MASConstraint

    MASConstraint 虽然是以 Constraint 结尾,但是其是 NSObject 的子类。

    该类的巧妙设计使得 Masonry 框架中的方法可以链式调用,对于有参数的方法,无法使用点语法进行调用,所以在该类中通过返回包含参数的 block 来进行参数的传递。

    可以将该类中的方法分为三类:

    1. 返回 block 的方法

      如下两个方法,分别表示对偏移量和约束属性的修改:

      - (MASConstraint * (^)(CGFloat))offset {
          return ^id(CGFloat offset){
              self.offset = offset;
              return self;
          };
      }
      
      - (MASConstraint * (^)(id))equalTo {
          return ^id(id attribute) {
              return self.equalToWithRelation(attribute, NSLayoutRelationEqual);
          };
      }	
      

      如同第一个方法,在返回的 block 中会使用调用时随后的指定参数对变量进行赋值,而对于类似第二个方法的对参照关系属性的修改,同样是在 block 中实现的。而其所使用的方法,在本类中实际是抽象方法,需要子类去具体实现。

    2. 返回本类实例的方法

      诸如 leftrighttopbottom 等方法,实际是返回了一个 MASConstraint 实例对象,从而可以进一步调用上面所说的第一种类型的方法。

      - (MASConstraint *)addConstraintWithLayoutAttribute:(NSLayoutAttribute __unused)layoutAttribute {
          MASMethodNotImplemented();
      }
      
      - (MASConstraint *)left {
          return [self addConstraintWithLayoutAttribute:NSLayoutAttributeLeft];
      }
      

      这些方法,实际都是对 addConstraintWithLayoutAttribute: 方法的调用,而其也是由子类实现的。

    3. 抽象方法

      该类中定义了诸多抽象方法,由子类来实现。

    从上面的分析可知,该类的主要功能就是实现链式编程,而具体的约束创建则是由子类实现的。主要关注 addConstraintWithLayoutAttribute:equalToWithRelation 方法。

    MASViewConstraint

    MASViewConstraint 作为 MASConstraint 的子类,除了继承父类的相关属性外,还定义了 firstViewAttributesecondViewAttribute 两个属性,其实际就是约束相关的两个参照。

    除了一个初始化方法外,还提供了一个类方法,用来获取所有通过该框架添加到一个视图中的约束。其是通过运行时函数,为 UIView 对象添加了一个属性 mas_installedConstraints 来保存所有通过该框架添加到该视图中的约束。

    + (NSArray *)installedConstraintsForView:(MAS_VIEW *)view {
        return [view.mas_installedConstraints allObjects];
    }
    

    虽然 MASViewConstraint 并不直接描述约束,但是其实际上包含了所有创建约束的条件,并且也仅仅表示一个约束。该类中定义了一个 NSLayoutConstraint 实例来保存该类的实例对象生成的具体约束,并且这个约束生效后,该实例对象会被添加到 mas_installedConstraints 属性中。不过,这个过程并不是自动的,而是需要调用 install 方法。

    在 iOS 8.0 后,约束支持 active 属性,可以通过设置该属性的值来取代向视图中添加约束的操作。

    if ([self supportsActiveProperty] && self.layoutConstraint) {
        self.layoutConstraint.active = YES;
        [self.firstViewAttribute.view.mas_installedConstraints addObject:self];
        return;
    }
    

    但是,self.layoutConstraint 第一次创建,或者不支持 active 时,仍然需要执行下面的操作。

    if (!self.firstViewAttribute.isSizeAttribute && !self.secondViewAttribute) {
        secondLayoutItem = self.firstViewAttribute.view.superview;
        secondLayoutAttribute = firstLayoutAttribute;
    }
    

    增加一个判断,当不是对视图自身尺寸的约束,而又没有参照时,则默认参照父视图。

    接着,使用相关属性创建约束 MASLayoutConstraint 实例。

    然后,获取应该添加该约束的视图,如果要求更新与之相同的约束,那么先遍历所有约束,找到与之相同的约束,直接更新即可,否则,将该约束添加到视图中,所以每个约束生效之前不会主动进行查重,即可能存在重复的约束。

    该类中重要的是重写的两个方法 addConstraintWithLayoutAttribute:equalToWithRelation

    - (MASConstraint *)left {
        return [self addConstraintWithLayoutAttribute:NSLayoutAttributeLeft];
    }
    
    - (MASConstraint *)addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {
        NSAssert(!self.hasLayoutRelation, @"Attributes should be chained before defining the constraint relation");
    
        return [self.delegate constraint:self addConstraintWithLayoutAttribute:layoutAttribute];
    }
    
    - (MASConstraint * (^)(id))equalTo {
        return ^id(id attribute) {
            return self.equalToWithRelation(attribute, NSLayoutRelationEqual);
        };
    }	
    
    - (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;
            }
        };
    }
    

    方法 equalToWithRelation 是没有参数的,但是 equalTo 方法返回的 block 中调用该方法时,似乎是传递了两个参数,这实际是同样的道理,参数实际是传递给 equalToWithRelation 函数返回的 block 的。

    如果参数 attribute 是数组,即同时要添加多个约束,那么便创建一个 MASCompositeConstraint 实例来保存这些约束的相关属性。并且,这里将代理同样赋值给该实例。

    从重写的 addConstraintWithLayoutAttribute: 可知,该代理实现了新增约束的操作,十分重要,其究竟是哪个类实现的呢!MASCompositeConstraint 作为 MASViewConstraint 的集合和代理,是否实现了新增约束的操作呢。

    MASCompositeConstraint

    MASCompositeConstraintMASConstraint 的子类,由一组 MASViewConstraint 初始化得到。其对父类抽象方法的实现,实际就是对其所持有的每个 MASViewConstraint 实例执行相应方法。即,该类可以对一组约束执行相同的操作。

    - (MASConstraint *)addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {
        [self constraint:self addConstraintWithLayoutAttribute:layoutAttribute];
        return self;
    }
    
    - (MASConstraint *)constraint:(MASConstraint __unused *)constraint addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {
        id<MASConstraintDelegate> strongDelegate = self.delegate;
        MASConstraint *newConstraint = [strongDelegate constraint:self addConstraintWithLayoutAttribute:layoutAttribute];
        newConstraint.delegate = self;
        [self.childConstraints addObject:newConstraint];
        return newConstraint;
    }
    

    其实现的代理函数,依然只是将消息传递给了原本的代理对象。那么,这个真正的代理是谁,又执行了什么操作呢。

    MASConstraintMaker

    MASConstraintMaker 的属性来看,似乎同 MASConstraint 类很是相同,但它是 NSObject 的子类,和 MASConstraint 并没有继承关系。

    类似的该类中有如下方法:

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

    MASViewConstraint 中不同的是,这里的 addConstraintWithLayoutAttribute: 方法直接调用了自身的 constraint:addConstraintWithLayoutAttribute: 方法,而不是代理的相应方法。

    这方法是 MASConstraintDelegate 代理声明的,且当前类实现如下:

    - (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;
            [self constraint:constraint shouldBeReplacedWithConstraint:compositeConstraint];
            return compositeConstraint;
        }
        if (!constraint) {
            newConstraint.delegate = self;
            [self.constraints addObject:newConstraint];
        }
        return newConstraint;
    }
    
    - (void)constraint:(MASConstraint *)constraint shouldBeReplacedWithConstraint:(MASConstraint *)replacementConstraint {
        NSUInteger index = [self.constraints indexOfObject:constraint];
        NSAssert(index != NSNotFound, @"Could not find constraint %@", constraint);
        [self.constraints replaceObjectAtIndex:index withObject:replacementConstraint];
    }
    

    可知,其根据自身关联的 view 和参数提供的位置属性创建一个 MASViewConstraint 实例对象,并且如果该方法的执行是由存在的 MASViewConstraint 实例引发的(即 constraint 参数存在),那么将两者合为一个 MASCompositeConstraint 实例对象,并替代引发该操作的实例。

    最后,将得到的实例保存到 self.constraints 中。

    该类同样定义了 install 方法,用来创建具体的约束。

    - (NSArray *)install {
        if (self.removeExisting) {
            NSArray *installedConstraints = [MASViewConstraint installedConstraintsForView:self.view];
            for (MASConstraint *constraint in installedConstraints) {
                [constraint uninstall];
            }
        }
        NSArray *constraints = self.constraints.copy;
        for (MASConstraint *constraint in constraints) {
            constraint.updateExisting = self.updateExisting;
            [constraint install];
        }
        [self.constraints removeAllObjects];
        return constraints;
    }
    

    但是,这里会先根据 self.removeExisting 属性来判断是否要将关联的 view 中约束清空。

    而且属性 self.updateExisting 同样是判断是否更新已存在约束的依据,这里更新的只是 NSLayoutConstraintconstant 属性,实际如果其他属性不同,则不会被认为是同一个约束。

    并且,该方法会清空 self.constraints 中的成员,表示所有约束创建完毕。

    那么,我们现在可以直接创建 MASConstraint 子类对象,并调用 install 方法来创建约束,也可以使用 MASConstraintMaker 来创建约束,那么那种方式更好呢。

    实际上,如果直接使用 MASConstraint 来创建约束是无法使用链式编程的,因为其返回的 block 执行时,实际是向代理传递了消息,而此时代理并没有设置。

    View+MASAdditions.h

    Masonry 框架为 UIView 创建了分类,方便约束的创建。理解上面的约束创建过程,再来看这几个方法,则一目了然了。

    - (NSArray *)mas_makeConstraints:(void(^)(MASConstraintMaker *))block {
        self.translatesAutoresizingMaskIntoConstraints = NO;
        MASConstraintMaker *constraintMaker = [[MASConstraintMaker alloc] initWithView:self];
        block(constraintMaker);
        return [constraintMaker install];
    }
    
    - (NSArray *)mas_updateConstraints:(void(^)(MASConstraintMaker *))block {
        self.translatesAutoresizingMaskIntoConstraints = NO;
        MASConstraintMaker *constraintMaker = [[MASConstraintMaker alloc] initWithView:self];
        constraintMaker.updateExisting = YES;
        block(constraintMaker);
        return [constraintMaker install];
    }
    
    - (NSArray *)mas_remakeConstraints:(void(^)(MASConstraintMaker *make))block {
        self.translatesAutoresizingMaskIntoConstraints = NO;
        MASConstraintMaker *constraintMaker = [[MASConstraintMaker alloc] initWithView:self];
        constraintMaker.removeExisting = YES;
        block(constraintMaker);
        return [constraintMaker install];
    }
    

    添加、更新、重置约束,都是根据 MASConstraintMaker 中的属性来控制的。

    [self.button mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.top.equalTo(@20);
    }];
    

    在 block 中,make.left 返回的是 MASConstraint 实例,之后调用 top 方法,实际是调用了 delegate 的 constraint:addConstraintWithLayoutAttribute: 方法,而该代理实际就是 make 对象,然后返回了一个 MASCompositeConstraint 实例对象,调用 equalTo 方法,进而调用了如下方法:

    - (MASConstraint * (^)(id, NSLayoutRelation))equalToWithRelation {
        return ^id(id attr, NSLayoutRelation relation) {
            for (MASConstraint *constraint in self.childConstraints.copy) {
                constraint.equalToWithRelation(attr, relation);
            }
            return self;
        };
    }
    

    这样,便实现了对多个约束设置相同偏移量的操作。

    展开全文
  • Masonry(Github)文档翻译

    2018-07-13 14:21:54
    Masonry以更漂亮的语法封装了AutoLayout,是一个轻量级的布局框架。 Masonry有自己的布局DSL(domain-specific language),提供了一种链式写法来描述NSLayoutConstraints,这使得布局代码变得更加简洁和可读。 ...

    Masonry以更漂亮的语法封装了AutoLayout,是一个轻量级的布局框架。
    Masonry有自己的布局DSL(domain-specific language),提供了一种链式写法来描述NSLayoutConstraints,这使得布局代码变得更加简洁和可读。
    Masonry 支持 iOS 和 macOS。


    NSLayoutConstraints怎么了?

    在底层AutoLayout是一种很强大灵活的用于管理和布局视图的方式。但用代码创建约束(Constraints)显得非常冗长且不易描述。设想这样的简单场景:在父视图中填放一个子视图,且每一边留下10像素的边距。

    UIView *superview = self.view;
    
    UIView *view1 = [[UIView alloc] init];
    view1.translatesAutoresizingMaskIntoConstraints = NO;
    view1.backgroundColor = [UIColor greenColor];
    [superview addSubview:view1];
    
    UIEdgeInsets padding = UIEdgeInsetsMake(10, 10, 10, 10);
    [superview addConstraints:@[
    //view1 constraints
    [NSLayoutConstraint constraintWithItem:view1
                                 attribute:NSLayoutAttributeTop
                                 relatedBy:NSLayoutRelationEqual
                                    toItem:superview
                                 attribute:NSLayoutAttributeTop
                                multiplier:1.0
                                  constant:padding.top],
    
    [NSLayoutConstraint constraintWithItem:view1
                                 attribute:NSLayoutAttributeLeft
                                 relatedBy:NSLayoutRelationEqual
                                    toItem:superview
                                 attribute:NSLayoutAttributeLeft
                                multiplier:1.0
                                  constant:padding.left],
    
    [NSLayoutConstraint constraintWithItem:view1
                                 attribute:NSLayoutAttributeBottom
                                 relatedBy:NSLayoutRelationEqual
                                    toItem:superview
                                 attribute:NSLayoutAttributeBottom
                                multiplier:1.0
                                  constant:-padding.bottom],
    
    [NSLayoutConstraint constraintWithItem:view1
                                 attribute:NSLayoutAttributeRight
                                 relatedBy:NSLayoutRelationEqual
                                    toItem:superview
                                 attribute:NSLayoutAttributeRight
                                multiplier:1
                                  constant:-padding.right],
    
     ]];
    

    即使这样简单的一个例子,代码就变得这么冗长,如果再多两三个视图,就立刻变得可读性很差。
    另外一个选择是使用VFL,稍微不那么长和绕。但是ASKII类型的语法有它自己的隐患,并且动画很麻烦因为NSLayoutConstraint constraintsWithVisualFormat:返回的是一个数组。


    准备好见你的Maker!

    以下是用MASConstraintMaker创建的简单约束:

        UIEdgeInsets padding = UIEdgeInsetsMake(10, 10, 10, 10);
    
        [view1 mas_makeConstraints:^(MASConstraintMaker *make) {
            make.top.equalTo(superview.mas_top).with.offset(padding.top);
            //with is an optional semantic filler
            make.left.equalTo(superview.mas_left).with.offset(padding.left);
            make.bottom.equalTo(superview.mas_bottom).with.offset(-padding.bottom);
            make.right.equalTo(superview.mas_right).with.offset(-padding.right);
        }];

    还可以更短:

     [view1 mas_makeConstraints:^(MASConstraintMaker *make) {
          make.edges.equalTo(superview).with.insets(padding);
    }];

    不过需要注意在第一个例子中我们需要把约束添加到superview中,尽管Masonry会自动添加约束到合适的view中。
    Masonry也会自动调用

    view1.translatesAutoresizidngMaskIntoConstraints = NO;

    不是所有的事情都是平等的

    .equalTo 等价于 NSLayoutRelationEqual
    .lessThanOrEqualTo 等价于 NSLayoutRelationLessThanOrEqual
    .greaterThanOrEqualTo 等价于 NSLayoutRelationGreaterThanOrEqual

    这3个等价关系接受一个参数,可以是下面的任何一种:

    1、MASViewAttribute

    make.centerX.lessThanOrEqualTo(view2.mas_left);
    

    以下是MASViewAttribute和NSLayoutAttribute 的对应关系:

    MASViewAttribute NSLayoutAttribute
    view.mas_left NSLayoutAttributeLeft
    view.mas_right NSLayoutAttributeRight
    view.mas_top NSLayoutAttributeTop
    view.mas_bottom NSLayoutAttributeBottom
    view.mas_leading NSLayoutAttributeLeading
    view.mas_trailing NSLayoutAttributeTrailing
    view.mas_width NSLayoutAttributeWidth
    view.mas_height NSLayoutAttributeHeight
    view.mas_centerX NSLayoutAttributeCenterX
    view.mas_centerY NSLayoutAttributeCenterY
    view.mas_baseline NSLayoutAttributeBaseline

    2. UIView/NSView

    if you want view.left to be greater than or equal to label.left :
    //这2中约束是等价的
    make.left.greaterThanOrEqualTo(label);
    make.left.greaterThanOrEqualTo(label.mas_left);

    3. NSNumber

    Auto Layout允许宽和高设置为常量。如果想设置view的最小和最大宽度,可以传一个number值:

    //width >= 200 && width <= 400
    make.width.greaterThanOrEqualTo(@200);
    make.width.lessThanOrEqualTo(@400);

    然而Auto Layout 不允许left, right, centerY等的对齐属性设置成常量值。所以,给这些属性传NSNumber,Masonry会转换到view的superview中去,比如:

    //creates view.left = view.superview.left + 10
    make.left.lessThanOrEqualTo(@10)

    除了NSNumber,也可以用原始值或结构体来建立约束,如:

    make.top.mas_equalTo(42);
    make.height.mas_equalTo(20);
    make.size.mas_equalTo(CGSizeMake(50, 100));
    make.edges.mas_equalTo(UIEdgeInsetsMake(10, 0, 10, 0));
    make.left.mas_equalTo(view).mas_offset(UIEdgeInsetsMake(10, 0, 10, 0));

    一般支持自动装箱(autoboxing)的宏会有mas_前缀。没前缀的版本在导入Masronry前,通过定义MAS_SHORTHAND_GLOBALS,也是可以用的。

    4. NSArray

    包含前面任何类型元素的数组也是可以的。

    make.height.equalTo(@[view1.mas_height, view2.mas_height]);
    make.height.equalTo(@[view1, view2]);
    make.left.equalTo(@[view1, @100, view3.right]);

    考虑优先级(prioritize)

    .priority 允许你指定一个确定的优先级
    .priorityHigh 等价于 UILayoutPriorityDefaultHigh
    .priorityMedium 介于高优先级和低优先级之间
    .priorityLow 等价于UILayoutPriorityDefaultLow

    优先级可以添加在约束链后面,像这样:

    make.left.greaterThanOrEqualTo(label.mas_left).with.priorityLow();
    make.top.equalTo(label.mas_top).with.priority(600);

    结构,结构,结构

    Masonry也提供了一些简便方法来同时创建多种约束。这些被称为MASCompositeConstraints

    edges

    // make top, left, bottom, right equal view2

    make.edges.equalTo(view2);
    

    // make top = superview.top + 5, left = superview.left + 10,
    // bottom = superview.bottom - 15, right = superview.right - 20

    make.edges.equalTo(superview).insets(UIEdgeInsetsMake(5, 10, 15, 20))
    

    size

    // make width and height greater than or equal to titleLabel

    make.size.greaterThanOrEqualTo(titleLabel)
    

    // make width = superview.width + 100, height = superview.height - 50

    make.size.equalTo(superview).sizeOffset(CGSizeMake(100, -50))
    

    center

    // make centerX and centerY = button1

    make.center.equalTo(button1)
    

    // make centerX = superview.centerX - 5, centerY = superview.centerY + 10

    make.center.equalTo(superview).centerOffset(CGPointMake(-5, 10))
    

    为了可读性高也可以把属性连起来像这样:

    // All edges but the top should equal those of the superview

    make.left.right.and.bottom.equalTo(superview);
    make.top.equalTo(otherView);

    让美好持续发生

    有时你需要修改已存在的约束来支持动画或者移除、替换约束。在Masonry中有一些不同的方式来更新约束。

    1、引用

    可以通过创建变量或者属性来引用一个特定的约束。也可以通过数组保存的方式来引用多个约束。

    // in public/private interface

      @property (nonatomic, strong) MASConstraint *topConstraint;
    
      ...
    

    // when making constraints

    [view1 mas_makeConstraints:^(MASConstraintMaker *make) {
        self.topConstraint = make.top.equalTo(superview.mas_top).with.offset(padding.top);
        make.left.equalTo(superview.mas_left).with.offset(padding.left);
    }];
    
    ...

    // 然后你可以调用

     [self.topConstraint uninstall];
    

    2. mas_updateConstraints

    如果只是更新约束中的常量值,你可以用这个mas_updateConstraints方法来代替mas_makeConstraints
    // 这是苹果推荐的用来添加或更新约束的地方
    // 这个方法会在响应setNeedsUpdateConstraints时被多次调用
    // 会被UIKit内部或者在你的代码中当你需要触发约束的更新时调用

    - (void)updateConstraints {
        [self.growingButton mas_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);
        }];
    
        //according to apple super should be called at end of method
        [super updateConstraints];
    }

    3、mas_remakeConstraints

    mas_updateConstraints在更新一组约束时很有用,但是做一些更新约束值之外的事就显得力不从心了。这就是为何要引入mas_remakeConstraints

    mas_remakeConstraints类似mas_updateConstraints,但是不同于更新常量值,它会在重新安装约束前移除所有的约束。这就让你能提供不同的约束,而不用时刻记住你要移除哪个约束了。

    - (void)changeButtonPosition {
        [self.button mas_remakeConstraints:^(MASConstraintMaker *make) {
            make.size.equalTo(self.buttonSize);
    
            if (topLeft) {
                make.top.and.left.offset(10);
            } else {
                make.bottom.and.right.offset(-10);
            }
        }];
    }

    出现麻烦咋整!

    布局并不能像你意料的那样,所以当事情真的搞砸了的时候,你肯定不希望在控制台看到这样的输出:

    Unable to simultaneously satisfy constraints.....blah blah blah....
    (
        "<NSLayoutConstraint:0x7189ac0 V:[UILabel:0x7186980(>=5000)]>",
        "<NSAutoresizingMaskLayoutConstraint:0x839ea20 h=--& v=--& V:[MASExampleDebuggingView:0x7186560(416)]>",
        "<NSLayoutConstraint:0x7189c70 UILabel:0x7186980.bottom == MASExampleDebuggingView:0x7186560.bottom - 10>",
        "<NSLayoutConstraint:0x7189560 V:|-(1)-[UILabel:0x7186980]   (Names: '|':MASExampleDebuggingView:0x7186560 )>"
    )
    
    Will attempt to recover by breaking constraint
    <NSLayoutConstraint:0x7189ac0 V:[UILabel:0x7186980(>=5000)]>

    Masonry在NSLayoutConstraint增加了一个分类,覆盖了- (NSString )description*的默认实现。这样在给约束和view起名字后,能够很轻松的从中找出哪些是Masonry创建的约束。

    这意味着你的控制台输出看起来是这样的:

    Unable to simultaneously satisfy constraints......blah blah blah....
    (
        "<NSAutoresizingMaskLayoutConstraint:0x8887740 MASExampleDebuggingView:superview.height == 416>",
        "<MASLayoutConstraint:ConstantConstraint UILabel:messageLabel.height >= 5000>",
        "<MASLayoutConstraint:BottomConstraint UILabel:messageLabel.bottom == MASExampleDebuggingView:superview.bottom - 10>",
        "<MASLayoutConstraint:ConflictingConstraint[0] UILabel:messageLabel.top == MASExampleDebuggingView:superview.top + 1>"
    )
    
    Will attempt to recover by breaking constraint
    <MASLayoutConstraint:ConstantConstraint UILabel:messageLabel.height >= 5000>

    应该在哪创建约束?

    @implementation DIYCustomView
    
    - (id)init {
        self = [super init];
        if (!self) return nil;
    
        // --- Create your views here ---
        self.button = [[UIButton alloc] init];
    
        return self;
    }
    
    // 告诉UIKit你在使用AutoLayout
    + (BOOL)requiresConstraintBasedLayout {
        return YES;
    }
    
    // 这是苹果推荐用来添加或更新约束的地方
    - (void)updateConstraints {
    
        // 在这里更新或者添加约束
        [self.button remakeConstraints:^(MASConstraintMaker *make) {
            make.width.equalTo(@(self.buttonSize.width));
            make.height.equalTo(@(self.buttonSize.height));
        }];
    
        //根据苹果介绍super方法应该在方法的最后调用一下
        [super updateConstraints];
    }
    
    - (void)didTapButton:(UIButton *)button {
        // --- Do your changes ie change variables that affect your layout etc ---
        self.buttonSize = CGSize(200, 200);
    
        // 通知约束他们应该被更新了
        [self setNeedsUpdateConstraints];
    }
    
    @end

    安装:

    使用CocoaPods.
    在Podfile文件中写:

    pod ‘Masonry'  
    

    即可。

    如果你在用Masonry时不想见到讨厌的mas_前缀,就添加

    #define MAS_SHORTHAND
    

    到prefix.pch,并导入到Masonry中。


    便捷代码块

    将以下代码块拷贝到~/Library/Developer/Xcode/UserData/CodeSnippets,写Masonry代码爽到飞起:

    mas_make -> [<view> mas_makeConstraints:^(MASConstraintMaker *make){<code>}];
    mas_update -> [<view> mas_updateConstraints:^(MASConstraintMaker *make){<code>}];
    mas_remake -> [<view> mas_remakeConstraints:^(MASConstraintMaker *make){<code>}];

    特性:

    1、不只是Auto Layout的子集,任何NSLayoutConstraint能做的,Masonry都能做
    2、很棒的debug支持,只要你给约束和view取好有意义的名字
    3、约束读起来像自然语句,通俗易懂
    4、没有令人抓狂的宏魔法。Masonry有了宏不会污染全局的名字空间。
    5、不基于字符串和字典,所以编译的时候就能检查出问题。

    展开全文
  • Masonry日常使用

    千次阅读 2019-10-03 09:35:43
    之前用frame纯代码布局,有很大的缺陷,比如我之前写的app在其他iphone(之前模拟机事xr)上跑,“身材”完全走形,通过学长的督促,这种学习了Masonry,一种约束布局,下面我来简单介绍一下。 这里有一个注意点...

    之前用frame纯代码布局,有很大的缺陷,比如我之前写的app在其他iphone(之前模拟机事xr)上跑,“身材”完全走形,通过学长的督促,这种学习了Masonry,一种约束布局,下面我来简单介绍一下。
    在这里插入图片描述
    这里有一个注意点,self.view addSubview 这句话必须写在约束之前,要不然就会报错。
    还有写约束的时候,注意根据UIView的坐标系,right和bottom进行了取反,所以offset得取负值
    在这里插入图片描述
    如果你要更新约束,注意用词,更新是在已存在的基础上,不能更新之前没有写过的约束。在这里插入图片描述
    这里button的大小按一下就会改变(原本大小和第一个绿色label一样)
    在这里插入图片描述

    [控件 mas_remakeConstraints:^(MASConstraintMaker *make) {
    //这个方法会将以前的约束全部删除,添加新的约束
    }];
    

    如果你要加一些约束,就用上面的方法。
    以上是非常基本的用法,高级用法敬请期待。

    展开全文
  • Masonry

    2019-09-15 09:03:29
    本周学习了Masonry,我感觉他就是用来设置view的大小及所处位置,以整个屏幕为参照,所以可以做到适配任何屏幕大小 文章参考https://www.jianshu.com/p/587efafdd2b3 主要用到三种约束: 添加约束 mas_...

    本周学习了Masonry,我感觉他就是用来设置view的大小及所处位置,以整个屏幕为参照,所以可以做到适配任何屏幕大小
    文章参考https://www.jianshu.com/p/587efafdd2b3
    用Masonry写的demo的github链接https://github.com/puyuerong/Masnory

    主要用到三种约束:

    1. 添加约束
      mas_makeConstraints()
    2. 移除之前的约束,重新添加新的约束
      mas_remakeConstraints()
    3. 更新约束,写哪条更新哪条,其他约束不变
      mas_updateConstraints()

    用到的约束

    • 设置边距
      • make.left.equalTo(self.view).with.offset(10);
        make.top.equalTo(self.view).with.offset(10);
        make.right.equalTo(self.view).with.offset(-10);
        make.bottom.equalTo(self.view).with.offset(-10);
      • 这样写下、右不需要写负号,insets方法中已经为我们做了取反的操作了。
        make.edges.equalTo(self.view).with.insets(UIEdgeInsetsMake(10, 10, 10, 10));
      • 设置长宽
        make.center.equalTo(self.view);
        make.width.mas_equalTo(100);
        make.height.mas_equalTo(100);
        这种可以通多设置屏幕的倍数设置大小,一下语句表示宽度是屏幕宽度的0.2倍
        make.width.equalTo(self.view).multipliedBy(0.2);

      注意 :在使用Masonry添加约束之前,需要在addSubview方法之后才能使用,否则会导致崩溃。

    展开全文
  • Masonry是最流行的瀑布流插件之一,配置简单,功能强大,在Github上收获了1.3w+ stars。如果你想使用瀑布流提升网站体验,Masonry将是不错的选择。 啥是瀑布流: 是比较流行的一种网站页面布局,视觉表现为参差不齐...
  • 从一开始的纯代码计算frame,虽然自认为计算frame 刚刚的,但是到后来还是开始xib的自动约束和手动约束与frame搭配使用,经历这几种方式,大概一年前开始普遍使用masonry来代码约束之后也跃跃欲试的自己体验了把,...
  • Masonry(一)

    2018-05-07 10:32:08
    Masonry is still actively maintained, we are committed to fixing bugs and merging good quality PRs from the wider community. However if you’re using Swift in your project, we recommend using Sn...
  • Masonry Label的宽度自适应

    万次阅读 2016-09-30 15:26:39
    当 label想要宽度自适应时 加上下面的话 “Content Compression Resistance”和“Content Hugging”1.宽度不够时[_label1 setContentCompressionResistancePriority:UILayoutPriorityRequired ...
  • Product -&gt; Scheme -&gt; Manage Schemes. 到这里删除相应的Product. 重新编译。
  • iOS 用masonry自适应label高度和宽度

    千次阅读 2018-09-20 15:43:28
    label.preferredMaxLayoutWidth = width;// 如果是多行的话给一个maxWidth [yourLabel setContentHuggingPriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisHorizontal];
  • cannot call methods on masonry prior to initialization; attempted to call method 'appended' 找到解决问题,需要在使用的时候,初始化一次,代码。 错误用法: $('#itemshere').masonry( 'appended
  • Masonry使用-如何约束宽高比

    万次阅读 2016-06-08 12:19:34
    打开Masonry的源代码,可以发现两个属性 /* /**  * Sets the NSLayoutConstraint multiplier property  */ - (MASConstraint * (^)(CGFloat multiplier))multipliedBy; /**  * ...
  • 首先添加圆角的方式并没有变:- (void)setCornerOnTop { UIBezierPath *maskPath; maskPath = [UIBezierPath bezierPathWithRoundedRect:self.bounds byRoundingCorners:(UIRectCorne
  • iOS使用Masonry时进行动画操作

    万次阅读 2019-08-29 11:12:01
    最近稍微闲了一些,决定把之前漏掉的知识再补充一下,然后发现在使用Masonry的时候动画怎么实现呢?于是经过练习和查找相关方法,终于找到了正确的做法,希望能够对有需要的人提供一些帮助。 正文 简单来...
  • 是写在layoutSubviews 还是写在updateConstraints里面?如果是自定义cell的话高度怎么解决
  • 多个label,可变长情况下使用Masonry

    千次阅读 2016-11-24 11:33:29
    常见的masonry的使用是比较简单,可应付一些常见的布局。但有些布局是动态的,则需要一些masonry和自动布局的技巧,直接上图,如何实现图片中cell的布局。 下图中,播放量可变长(必须显示完整);绿底的是“出品人...
  • 这个是由于ajax添加上去的数据没有再次定位 解决方法: $.ajax({ ..... success:function (){ } ..... })
  • 适配iPhonex的头部安全区域Masonry 的适配安全区域 __weak typeof(self)weakself = self; [backview mas_makeConstraints:^(MASConstraintMaker *make) { if (@available(iOS 11.0,*)) { make.top.equalTo(we
  • Masonry对UIScrollView添加约束

    千次阅读 2016-12-26 13:50:51
    WS(weakSelf); // 1.把scrollView添加到控制器view [self.view addSubview:self.scrollView]; [_scrollView mas_makeConstraints:^(MASConstraintMaker *make) { make.edges.equalTo(weakSelf.vie
  • Masonry设置右边距

    千次阅读 2016-03-07 14:18:41
    设控件的右边距为10,设为负数就可以了。如下:  [line mas_makeConstraints:^(MASConstraintMaker *make) {  make.right.equalTo(@-10);  }];
1 2 3 4 5 ... 20
收藏数 9,219
精华内容 3,687
关键字:

masonry