天天看点

autolayout - sizeClass - Masonry - 1

作为极负盛名的autoLayout的开源框架:Masonry , 今天我们来学习学习

参考:http://www.cocoachina.com/ios/20141219/10702.html

http://www.tuicool.com/articles/RBbeqi

Masonry Github的地址:https://github.com/SnapKit/Masonry

摘自github:

Masonry is a light-weight layout framework which wraps AutoLayout with a nicer syntax. Masonry has its own layout DSL which provides a chainable way of describing your NSLayoutConstraints which results in layout code that is more concise and readable. Masonry supports iOS and Mac OS X.

Masonry是一种轻量级布局框架,它用优雅的语法封装了AutoLayout。Masonry拥有属于自己的特定领域布局语言,它提供了链式语法来描述NSLayoutConstraints,因此你的布局代码会更简洁和有更高的可读性。Masonry支持iOS和Mac OS X平台。

What's wrong with NSLayoutConstraints?

Under the hood Auto Layout is a powerful and flexible way of organising and laying out your views. However creating constraints from code is verbose and not very descriptive. Imagine a simple example in which you want to have a view fill its superview but inset by 10 pixels on every side

Auto Layout是一种强大和灵活的方式去管理和布局你的视图。然而创建代码的约束实在是冗长和描述性弱。试想一下一个简单的例子,你想要一个视图充斥着它的父视图但是每一边有着10px的边距。

UIView *superview = self;

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],

 ]];           

Even with such a simple example the code needed is quite verbose and quickly becomes unreadable when you have more than 2 or 3 views. Another option is to use Visual Format Language (VFL), which is a bit less long winded. However the ASCII type syntax has its own pitfalls and its also a bit harder to animate as NSLayoutConstraint constraintsWithVisualFormat: returns an array.

即使是这么简单的例子相关的编码都需要如此冗杂和迅速变得可读性很低当你再拥有2到3个视图的时候,另外一个选择就是使用可视化格式语言(VFL),会相应低少一点冗杂。然而,ASCII类型语法有着它自身的隐患并且它比较难于用设置动画作为NSLayoutConstraint constraintsWithVisualFormat:返回一个数组对象。

Prepare to meet your Maker!

Heres the same constraints created using MASConstraintMaker: 一样的约束创建当你使用MASConstraintMaker的时候:

<span style="font-size:14px;">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);
}];</span>           

或者更加简短:

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

Also note in the first example we had to add the constraints to the superview [superview addConstraints:.... Masonry however will automagically add constraints to the appropriate view.

Masonry will also call view1.translatesAutoresizingMaskIntoConstraints = NO; for you.

同时注意到第一个例子我们需要添加约束至superview,然而Masonry会自动地帮我们添加约束项至适当的视图上。 Masonry也会自动调用view1.translatesAutoresizingMaskIntoConstraints = NO;

Not all things are created equal

.equalTo

 equivalent to NSLayoutRelationEqual

.lessThanOrEqualTo

 equivalent to NSLayoutRelationLessThanOrEqual

.greaterThanOrEqualTo

 equivalent to NSLayoutRelationGreaterThanOrEqual

These three equality constraints accept one argument which can be any of the following:

这三个等式约束项接受一个参数。

MASViewAttribute

接下来就布局属性了,重点

make.centerX.lessThanOrEqualTo(view2.mas_left);           
autolayout - sizeClass - Masonry - 1

if you want view.left to be greater than or equal to label.left :

//these two constraints are exactly the same
make.left.greaterThanOrEqualTo(label);
make.left.greaterThanOrEqualTo(label.mas_left);           

NSNumber

Auto Layout allows width and height to be set to constant values. if you want to set view to have a minimum and maximum width you could pass a number to the equality blocks:

AutoLayout允许将width和height设置成常量值。如果你想将view设置成一个拥有最小值或最大值的宽度你可以传递一个数值给等式blocks:

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

However Auto Layout does not allow alignment attributes such as left, right, centerY etc to be set to constant values. So if you pass a NSNumber for these attributes Masonry will turn these into constraints relative to the view’s superview ie:

虽然Auto Layout不允许将对齐属性例如left,right,centerY等等设置成常量值。所以如果你传递一个NSNumber值给这些属性Masonry将会自动将它们转变成关于它们父类视图的相关约束项。

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

就是Auto Layout是允许直接设定宽高为常量值的,但是如果对于alignment属性,如左,右边框,中心点X,Y等是不能够直接指定常量值的,想想也无法指定哦,所以当你直接指定了一个常量值,它会自动语义上帮你转变成上面的代码形式。

Instead of using NSNumber, you can use primitives and structs to build your constraints, like so:

除了使用NSNumber,你也可以使用基本的数据类型和结构体(例如CGSize啦)来创建你的约束项,例如:

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));           

By default, macros which support autoboxing are prefixed with mas_.  Unprefixed versions are available by defining MAS_SHORTHAND_GLOBALS before importing Masonry.

默认地,支持autoboxing机制的宏被定义为前缀是mas_。没有前缀的版本被可用于当被定义为 MAS_SHORTHAND_GLOBALS 在导入Masonry之前。

NSArray

An array of a mixture of any of the previous types

混合任何原始的类型的数组

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

Learn to prioritize

学会如何安排优先级

先看看原生的优先级:

typedef float UILayoutPriority;
static const UILayoutPriority UILayoutPriorityRequired NS_AVAILABLE_IOS(6_0) = 1000; // A required constraint.  Do not exceed this.
static const UILayoutPriority UILayoutPriorityDefaultHigh NS_AVAILABLE_IOS(6_0) = 750; // This is the priority level with which a button resists compressing its content.
static const UILayoutPriority UILayoutPriorityDefaultLow NS_AVAILABLE_IOS(6_0) = 250; // This is the priority level at which a button hugs its contents horizontally.
static const UILayoutPriority UILayoutPriorityFittingSizeLevel NS_AVAILABLE_IOS(6_0) = 50; // When you send -[UIView systemLayoutSizeFittingSize:], the size fitting most closely to the target size (the argument) is computed.  UILayoutPriorityFittingSizeLevel is the priority level with which the view wants to conform to the target size in that computation.  It's quite low.  It is generally not appropriate to make a constraint at exactly this priority.  You want to be higher or lower.           

其实原生的约束优先级已经写得很详细了,但是我们还是来看看Masonry做了什么:

.priority

 allows you to specify an exact priority

.priorityHigh

 equivalent to UILayoutPriorityDefaultHigh

.priorityMedium

 is half way between high and low

.priorityLow

 equivalent to UILayoutPriorityDefaultLow

Priorities are can be tacked on to the end of a constraint chain like so:

Priorities能够在约束链的尾端被指定如这样:

make.left.greaterThanOrEqualTo(label.mas_left).with.priorityLow();

make.top.equalTo(label.mas_top).with.priority(600);           

很显然,Masonry首先在约束优先级的级分上处理得更明晰,更明确,而在使用语法上也处理得更简单,简洁。

Composition, composition, composition

重要的事情说三遍,哈哈哈,可爱的外国人!

Masonry also gives you a few convenience methods which create multiple constraints at the same time. These are called MASCompositeConstraints

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))           

You can chain view attributes for increased readability:

你可以将视图的属性串联起来从而增加可读性:

// All edges but the top should equal those of the superview
make.left.right.and.bottom.equalTo(superview);
make.top.equalTo(otherView);           

Hold on for dear life

Sometimes you need modify existing constraints in order to animate or remove/replace constraints. In Masonry there are a few different approaches to updating constraints.

有时候你需要修改一些已经存在的约束项为得是去动态地移除/替换约束项。在Masonry上有一些不同的方法来更新约束项。

1. References

You can hold on to a reference of a particular constraint by assigning the result of a constraint make expression to a local variable or a class property. You could also reference multiple constraints by storing them away in an array.

你可以保持一个对特定约束的引用通过分配约束的结果来表达局部变量的的表达或者类属性。你也可以引用多重约束项通过将它们储存在数组里面。

// 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);
}];

...
// then later you can call
[self.topConstraint uninstall];           

2. mas_updateConstraints

Alternatively if you are only updating the constant value of the constraint you can use the convience method mas_updateConstraints instead of mas_makeConstraints

另外地如果你仅仅想要升级常量的约束你可以使用这个便捷的方法:mas_updateConstraints 和 mas_makeConstraints

// this is Apple's recommended place for adding/updating constraints
// this method can get called multiple times in response to setNeedsUpdateConstraints
// which can be called by UIKit internally or in your code if you need to trigger an update to your constraints
- (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 is useful for updating a set of constraints, but doing anything beyond updating constant values can get exhausting. That's where mas_remakeConstraints comes in.

mas_remakeConstraints is similar to mas_updateConstraints, but instead of updating constant values, it will remove all of its contraints before installing them again. This lets you provide different constraints without having to keep around references to ones which you want to remove.

mas_updateConstraints 对于更新constraint的设置非常好用,但是如果做一些除了更新常量值的操作就会显得非常无力。这就是为什么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);
        }
    }];
}           

You can find more detailed examples of all three approaches in the Masonry iOS Examples project. 你能在Masonry提供的demo找到更多的例子,但是呢,一打开工程就缺失了一个.a文件,还是得找找什么原因呢,是pod的原因?

When the ^&*[email protected] hits the fan!

Laying out your views doesn't always goto plan. So when things literally go pear shaped, you don't want to be looking at console output like this:

布局你的视图界面不是总是能跟着计划走,所以当布局结果由字面显示如下,你不会想着看到控制台的输出如下:

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 adds a category to NSLayoutConstraint which overrides the default implementation of - (NSString *)description. Now you can give meaningful names to views and constraints, and also easily pick out the constraints created by Masonry.

which means your console output can now look like this:

Masonry增加了一个类目给NSLayoutConstraint,这个扩展重载了默认的(NSString*)description实现。现在你可以给一个富有意义的名字给视图和相应的约束了,并且可以同时挑选出哪些是有Masonry所创建的。

怎么用CocoaPods下载就不翻译了直接上贴:

Installation

Use the orsome CocoaPods.

In your Podfile

pod 'Masonry'

If you want to use masonry without all those pesky 'mas_' prefixes. Add #define MAS_SHORTHAND to your prefix.pch before importing Masonry

#define MAS_SHORTHAND

Get busy Masoning

#import "Masonry.h"

Code Snippets

Copy the included code snippets to 

~/Library/Developer/Xcode/UserData/CodeSnippets

 to write your masonry blocks at lightning speed!

mas_make

 -> 

[<view> mas_makeConstraints:^(MASConstraintMaker *make){<code>}];

mas_update

 -> 

[<view> mas_updateConstraints:^(MASConstraintMaker *make){<code>}];

mas_remake

 -> 

[<view> mas_remakeConstraints:^(MASConstraintMaker *make){<code>}];

Features

  • Not limited to subset of Auto Layout. Anything NSLayoutConstraint can do, Masonry can do too!
  • Great debug support, give your views and constraints meaningful names.
  • Constraints read like sentences.
  • No crazy macro magic. Masonry won't pollute the global namespace with macros.
  • Not string or dictionary based and hence you get compile time checking.
  • 不受限制的Auto Layout 的子集, 所有NSLayoutConstraint能够做的,Masonry也能够做!
  • 极好的debug模式的支持,给你的视图和约束项更富意义的命名。
  • 将约束项设置得像句子一样好读。
  • 没有让人发狂的宏定义。Masonry不会用宏破坏全局的命名空间。
  • 没有基于字符串和字典的约束规则因此你可以在编译的时候就被检查(我们知道VFL规则错了仍可以通过编译)

收集的各个优秀的文章:

Masonry:

点击打开链接

点击打开链接

点击打开链接

点击打开链接

集成:

点击打开链接

进阶:

点击打开链接

例子:

点击打开链接