自動(dòng)布局與Masonry使用注意事項(xiàng)

個(gè)人Github博客析蝴,求關(guān)注

1 理解自身內(nèi)容尺寸約束與抗壓抗拉

自身內(nèi)容尺寸約束:一般來說禁漓,要確定一個(gè)視圖的精確位置是目,至少需要4個(gè)布局約束(以確定水平位置x妹萨、垂直位置y年枕、寬度w和高度h)。但是乎完,某些用來展現(xiàn)內(nèi)容的用戶控件熏兄,例如文本控件UILabel、按鈕UIButton树姨、圖片視圖UIImageView等摩桶,它們具有自身內(nèi)容尺寸(Intrinsic Content Size),此類用戶控件會(huì)根據(jù)自身內(nèi)容尺寸添加布局約束帽揪。也就是說硝清,如果開發(fā)者沒有顯式給出其寬度或者高度約束,則其自動(dòng)添加的自身內(nèi)容約束將會(huì)起作用转晰。因此看似“缺失”約束芦拿,實(shí)際上并非如此士飒。

關(guān)于自身內(nèi)容尺寸約束,簡單來說就是某些用來展現(xiàn)內(nèi)容的用戶控件蔗崎,它們會(huì)根據(jù)自身內(nèi)容尺寸添加布局約束酵幕。

自身內(nèi)容尺寸約束的抗擠壓與抗拉抻效果。彈簧會(huì)有自身固有長度缓苛,當(dāng)有外力作用時(shí)裙盾,彈簧會(huì)抵抗外力作用,盡量接近固有長度他嫡。
抗拉抻:當(dāng)外力拉長彈簧時(shí)番官,彈簧長度大于固有長度,且產(chǎn)生向內(nèi)收的力阻止外力拉抻钢属,且盡量維持長度接近自身固有長度徘熔。
抗擠壓:當(dāng)外力擠壓彈簧時(shí),彈簧長度小于固有長度淆党,且產(chǎn)生向外頂?shù)牧ψ柚雇饬D壓酷师,且盡量維持長度接近自身固有長度。

關(guān)于抗壓抗拉染乌,就是布局沖突需要犧牲某些控件的某些寬度或者高度約束時(shí)山孔,抗壓高的控件越不容易被壓縮,抗拉高的控件越不容易被拉升荷憋。即自身布局對(duì)抗外界布局的能力台颠。

樣例:

一種常見的業(yè)務(wù)場(chǎng)景是用戶修改地址,在輸入新地址之前先讀取用戶之前的地址作為填充勒庄。UI實(shí)現(xiàn)是水平平行的UILabel和UITextField串前。
代碼實(shí)現(xiàn)如下:

- (NSString *)aLongAddress
{
    return @"A long long long long long long long long long address";
}

- (NSString *)aShortAddress
{
    return @"A short address";
}

- (void)sampleCode
{
    UIView *layoutView = [UIView new];
    layoutView.frame = CGRectMake(0, 200, [UIScreen mainScreen].bounds.size.width, 100);
    layoutView.backgroundColor = [[UIColor alloc] initWithRed:0.5 green:0.5 blue:0.5 alpha:0.5];
    [self.view addSubview:layoutView];
    
    UILabel *address = [[UILabel alloc] init];
    [layoutView addSubview:address];
    address.text = @"地址:";
    address.backgroundColor = [UIColor blueColor];
    [address mas_makeConstraints:^(MASConstraintMaker *make) {
        make.centerY.equalTo(layoutView);
        make.left.equalTo(layoutView).offset(10);
    }];
    
    UITextField *addressTextField = [[UITextField alloc] init];
    [layoutView addSubview:addressTextField];
    addressTextField.returnKeyType = UIReturnKeyDone;
    addressTextField.font = [UIFont systemFontOfSize:15];
    addressTextField.clearButtonMode = UITextFieldViewModeWhileEditing;
    addressTextField.layer.borderWidth = 1 / [UIScreen mainScreen].scale;
    addressTextField.layer.borderColor =  [[[UIColor alloc] initWithRed:1 green:1 blue:0 alpha:1] CGColor];
    addressTextField.layer.cornerRadius = 3;
    addressTextField.text = [self aLongAddress];
    [addressTextField mas_makeConstraints:^(MASConstraintMaker *make) {
        make.height.equalTo(address);
        make.centerY.equalTo(address);
        make.right.equalTo(layoutView.mas_right).offset(-10);
        make.left.equalTo(address.mas_right).offset(10);
    }];
}

此處使用了UILabel的自身內(nèi)容尺寸約束,當(dāng)houseNumberTextField.text = [self aShortAddress]UI表現(xiàn)正常实蔽。

但荡碾,當(dāng)houseNumberTextField.text = [self aLongAddress]時(shí)會(huì)出現(xiàn)address UILabel被擠壓掉的情況,如下圖所示:

出錯(cuò)實(shí)例
出錯(cuò)實(shí)例

原因是address Label的水平抗壓縮沒有設(shè)置局装。

在address Label創(chuàng)建的時(shí)候添加如下代碼[address setContentCompressionResistancePriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisHorizontal]則顯示正常坛吁。

另,在某些情況下存在view被拉升铐尚,極有可能是沒有設(shè)置抗拉升拨脉,此處不一一列舉。

附塑径,抗壓抗拉相關(guān)API如下:


- (UILayoutPriority)contentHuggingPriorityForAxis:(UILayoutConstraintAxis)axis NS_AVAILABLE_IOS(6_0);
- (void)setContentHuggingPriority:(UILayoutPriority)priority forAxis:(UILayoutConstraintAxis)axis NS_AVAILABLE_IOS(6_0);

- (UILayoutPriority)contentCompressionResistancePriorityForAxis:(UILayoutConstraintAxis)axis NS_AVAILABLE_IOS(6_0);
- (void)setContentCompressionResistancePriority:(UILayoutPriority)priority forAxis:(UILayoutConstraintAxis)axis NS_AVAILABLE_IOS(6_0);

2 NSLayoutConstraint只能修改constant

NSLayoutConstraint即自動(dòng)布局的約束類女坑,它是自動(dòng)布局的關(guān)鍵之一。該類有如下屬性我們需要重點(diǎn)關(guān)注统舀。

NS_CLASS_AVAILABLE_IOS(6_0)
@interface NSLayoutConstraint : NSObject

// other code

@property UILayoutPriority priority;
@property BOOL shouldBeArchived;

/* accessors
 firstItem.firstAttribute {==,<=,>=} secondItem.secondAttribute * multiplier + constant
 */
@property (readonly, assign) id firstItem;
@property (readonly) NSLayoutAttribute firstAttribute;
@property (readonly) NSLayoutRelation relation;
@property (nullable, readonly, assign) id secondItem;
@property (readonly) NSLayoutAttribute secondAttribute;
@property (readonly) CGFloat multiplier;

/* Unlike the other properties, the constant may be modified after constraint creation.  Setting the constant on an existing constraint performs much better than removing the constraint and adding a new one that's just like the old but for having a new constant.
 */
@property CGFloat constant;

/* The receiver may be activated or deactivated by manipulating this property.  Only active constraints affect the calculated layout.  Attempting to activate a constraint whose items have no common ancestor will cause an exception to be thrown.  Defaults to NO for newly created constraints. */
@property (getter=isActive) BOOL active NS_AVAILABLE(10_10, 8_0);

// other code

@end

布局公式:firstItem.firstAttribute {==,<=,>=} secondItem.secondAttribute * multiplier + constant

解釋:firstItem與secondItem分別是界面中受約束的視圖與被參照的視圖匆骗。

注意:當(dāng)使用代碼來修改約束時(shí)劳景,只能修改約束的常量值constant。一旦創(chuàng)建了約束碉就,其他只讀屬性都是無法修改的盟广,特別要注意的是比例系數(shù)multiplier也是只讀的。

Masonry是基于NSLayoutConstraint等類的封裝瓮钥,也正是如此筋量,我們?cè)谡{(diào)用- (NSArray *)mas_updateConstraints:(void(^)(MASConstraintMaker *))block的時(shí)候也只能更新NSLayoutConstraint中的@property CGFloat constant

MASViewConstraint找到如下代碼可以佐證:

- (void)install {

    // other code
    
    MASLayoutConstraint *existingConstraint = nil;
    if (self.updateExisting) { //如果是update碉熄,則去匹配對(duì)應(yīng)的existingConstraint
        existingConstraint = [self layoutConstraintSimilarTo:layoutConstraint];
    }
    if (existingConstraint) { //找到了existingConstraint桨武,最終也只更新了existingConstraint.constant
        // just update the constant
        existingConstraint.constant = layoutConstraint.constant;
        self.layoutConstraint = existingConstraint;
    } else { //沒有找到existingConstraint,添加一個(gè)新的約束
        [self.installedView addConstraint:layoutConstraint];
        self.layoutConstraint = layoutConstraint;
        [firstLayoutItem.mas_installedConstraints addObject:self];
    }
}

// 除了constant锈津,其它都一樣的約束是Similar約束
- (MASLayoutConstraint *)layoutConstraintSimilarTo:(MASLayoutConstraint *)layoutConstraint {
    // check if any constraints are the same apart from the only mutable property constant

    // go through constraints in reverse as we do not want to match auto-resizing or interface builder constraints
    // and they are likely to be added first.
    for (NSLayoutConstraint *existingConstraint in self.installedView.constraints.reverseObjectEnumerator) {
        if (![existingConstraint isKindOfClass:MASLayoutConstraint.class]) continue;
        if (existingConstraint.firstItem != layoutConstraint.firstItem) continue;
        if (existingConstraint.secondItem != layoutConstraint.secondItem) continue;
        if (existingConstraint.firstAttribute != layoutConstraint.firstAttribute) continue;
        if (existingConstraint.secondAttribute != layoutConstraint.secondAttribute) continue;
        if (existingConstraint.relation != layoutConstraint.relation) continue;
        if (existingConstraint.multiplier != layoutConstraint.multiplier) continue;
        if (existingConstraint.priority != layoutConstraint.priority) continue;

        return (id)existingConstraint;
    }
    return nil;
}

樣例:

@interface ViewController ()

@property (nonatomic, strong) UILabel *lbl;
@property (nonatomic, strong) UIButton *btn;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    
    self.btn = [UIButton buttonWithType:UIButtonTypeCustom];
    self.btn.backgroundColor = [UIColor blueColor];
    [self.btn setTitle:@"按鈕" forState:UIControlStateNormal];
    [self.btn addTarget:self action:@selector(onTest:) forControlEvents:UIControlEventTouchDown];
    [self.view addSubview:self.btn];
    [self.btn mas_updateConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(self.view).offset(200);
        make.centerX.equalTo(self.view);
        make.size.mas_equalTo(CGSizeMake(100, 33));
    }];
    
    self.lbl = [[UILabel alloc] init];
    self.lbl.text = @"一個(gè)label";
    self.lbl.backgroundColor = [UIColor redColor];
    self.lbl.textAlignment = NSTextAlignmentCenter;
    [self.view addSubview:self.lbl];
    [self.lbl mas_updateConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(self.view).offset(300);
        make.centerX.equalTo(self.view);
        make.size.equalTo(self.btn);
    }];
}

- (void)onTest:(id)sender
{
    [self.lbl mas_updateConstraints:^(MASConstraintMaker *make) {
        make.size.mas_equalTo(CGSizeMake(200, 100));
    }];
}

@end

當(dāng)按鈕被按下時(shí)呀酸,控制臺(tái)出現(xiàn)如下警告

2016-08-03 18:49:13.110 layout[47924:2886276] Unable to simultaneously satisfy constraints.
 Probably at least one of the constraints in the following list is one you don't want. 
 Try this: 
  (1) look at each constraint and try to figure out which you don't expect; 
  (2) find the code that added the unwanted constraint or constraints and fix it. 
(
    "<MASLayoutConstraint:0x7ffecb632470 UIButton:0x7ffecb4f28e0.width == 100>",
    "<MASLayoutConstraint:0x7ffecb637550 UILabel:0x7ffecb637030.width == UIButton:0x7ffecb4f28e0.width>",
    "<MASLayoutConstraint:0x7ffecb71fc10 UILabel:0x7ffecb637030.width == 200>"
)

Will attempt to recover by breaking constraint 
<MASLayoutConstraint:0x7ffecb71fc10 UILabel:0x7ffecb637030.width == 200>

Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.
2016-08-03 18:49:13.111 layout[47924:2886276] Unable to simultaneously satisfy constraints.
 Probably at least one of the constraints in the following list is one you don't want. 
 Try this: 
  (1) look at each constraint and try to figure out which you don't expect; 
  (2) find the code that added the unwanted constraint or constraints and fix it. 
(
    "<MASLayoutConstraint:0x7ffecb612bc0 UIButton:0x7ffecb4f28e0.height == 33>",
    "<MASLayoutConstraint:0x7ffecb625300 UILabel:0x7ffecb637030.height == UIButton:0x7ffecb4f28e0.height>",
    "<MASLayoutConstraint:0x7ffecb486f10 UILabel:0x7ffecb637030.height == 100>"
)

Will attempt to recover by breaking constraint 
<MASLayoutConstraint:0x7ffecb486f10 UILabel:0x7ffecb637030.height == 100>

Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.

原因是,lbl創(chuàng)建時(shí)其size約束是make.size.equalTo(self.btn)琼梆,但btn被點(diǎn)擊時(shí)性誉,企圖去update size約束為make.size.mas_equalTo(CGSizeMake(200, 100)),然而無法找到existingConstraint茎杂,因此實(shí)際上是額外添加了一個(gè)約束make.size.mas_equalTo(CGSizeMake(200, 100))出現(xiàn)了布局沖突错览。

這件事可以這么看,NSLayoutConstraint只能修改constant決定了mas_updateConstraints的實(shí)現(xiàn)方式為:找到既有約束就去改變constant找不到既有約束就添加新約束煌往。

3 被Masonry布局的view一定要與比較view有共同的祖先view

這句話比較拗口倾哺,其中涉及三類view,解釋如下携冤。

  1. 被Masonry布局的view:執(zhí)行了- (NSArray *)mas_makeConstraints:(void(^)(MASConstraintMaker *make))block悼粮、- (NSArray *)mas_updateConstraints:(void(^)(MASConstraintMaker *make))block闲勺、- (NSArray *)mas_remakeConstraints:(void(^)(MASConstraintMaker *make))block等函數(shù)的view曾棕。
  2. 比較view:以上3函數(shù)block塊里面出現(xiàn)的view。
  3. 共同的祖先view:【1】和【2】的共同祖先view菜循。

樣例1:

- (void)sampleCode
{
    UIView *v0 = [UIView new];
    [self.view addSubview:v0];

    UIView *v1 = [UIView new];
    [v0 addSubview:v1];
    [v1 mas_makeConstraints:^(MASConstraintMaker *make) {
        make.size.mas_equalTo(CGSizeMake(10, 10));
    }];

    UIView *v2 = [UIView new];
    [v0 addSubview:v2];
    [v2 mas_makeConstraints:^(MASConstraintMaker *make) {
        make.size.equalTo(v1);
    }];
}

針對(duì)如下代碼塊來說

UIView *v2 = [UIView new];
[v0 addSubview:v2];
[v2 mas_makeConstraints:^(MASConstraintMaker *make) {
    make.size.equalTo(v1);
}];

v2是被Masonry布局的view翘地,v1是比較view,v0是共同的祖先view癌幕。

樣例2:

@implementation AutoLayoutViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    [self useMasonryWithoutSuperView];
}

- (void)useMasonryWithoutSuperView
{
    UIView *masView = [UIView new];
    [masView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.center.equalTo(self.view);
    }];
}

@end

以上代碼執(zhí)行時(shí)會(huì)crash衙耕,crash log如下:

2016-08-04 00:52:47.542 CommonTest[1731:22953] *** Assertion failure in -[MASViewConstraint install], /Users/shuncheng/SourceCode/SampleCode/AutoLayout/Pods/Masonry/Masonry/MASViewConstraint.m:338
2016-08-04 00:52:47.548 CommonTest[1731:22953] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'couldn't find a common superview for <UIView: 0x7fa59bd30dd0; frame = (0 0; 0 0); layer = <CALayer: 0x7fa59bd2f3c0>> and <UIView: 0x7fa59bd30c60; frame = (0 0; 414 736); autoresize = W+H; layer = <CALayer: 0x7fa59bd24780>>'

crash的原因顯而易見,即勺远,masView(被Masonry布局的view)與self.view(比較view)沒有共同祖先view橙喘,因?yàn)閙asView沒有父view,所以它和self.view必然沒有共同祖先view胶逢。

被Masonry布局的view沒有添加到superview上其實(shí)比較容易被發(fā)現(xiàn)厅瞎,最怕的是出現(xiàn)如樣例3一樣的鬼畜情況饰潜。

樣例3:

@implementation AutoLayoutViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    [self sampleCode];
}

- (void)sampleCode
{
    AutoLayoutViewController * __weak weakSelf = self;
    [fooNetworkModel fetchData:^{
        AutoLayoutViewController * self = weakSelf;
        [AutoLayoutViewController showSampleViewAtView:self.view];
    }];
}

+ (void)showSampleViewAtView:(UIView *)view
{
    UIView *v1 = [UIView new];
    [view addSubview:v1];
    [v1 mas_makeConstraints:^(MASConstraintMaker *make) {
        make.size.mas_equalTo(CGSizeMake(10, 10));
    }];
    
    UIView *v2 = [UIView new];
    [view addSubview:v2];
    [v2 mas_makeConstraints:^(MASConstraintMaker *make) {
        make.size.equalTo(v1);
    }];
}

@end

以上代碼通常不會(huì)出錯(cuò),但是一種異常情況是:在AutoLayoutViewController析構(gòu)后和簸,網(wǎng)絡(luò)數(shù)據(jù)返回彭雾,此時(shí)AutoLayoutViewController * self = weakSelfself == nil。執(zhí)行[AutoLayoutViewController showSampleViewAtView:nil]锁保,則會(huì)出現(xiàn)【樣例2】一樣的crash薯酝。

原因是:v1和v2都沒有添加到view上去(因?yàn)関iew為空)所以make.size.equalTo(v1)會(huì)出錯(cuò)(v1和v2沒有共同的父view)。由此也引申到weakSelf的副作用爽柒,即必須要確保weakSelf是nil時(shí)吴菠,執(zhí)行邏輯完全沒有問題(目前已經(jīng)兩次被坑)。

4 不要被update迷惑

這里說的update有兩層含義:

  1. UIView的方法- (void)updateConstraints NS_AVAILABLE_IOS(6_0)
  2. Masonry的方法- (NSArray *)mas_updateConstraints:(void(^)(MASConstraintMaker *make))block

這里先來討論一下UIView的- (void)updateConstraints方法浩村。

- (void)updateConstraints方法是用來更新view約束的橄务,它有一個(gè)常見的使用場(chǎng)景——批量更新約束。比如你的多個(gè)約束是由多個(gè)不同的property決定穴亏,每次設(shè)置property都會(huì)直接更新局部約束蜂挪。這樣效率不高。不如直接override- (void)updateConstraints方法嗓化,在方面里面對(duì)property進(jìn)行判斷棠涮,每次設(shè)置property的時(shí)候調(diào)用一下- (void)setNeedsUpdateConstraints。偽代碼如下:

優(yōu)化前:

@implementation AutoLayoutView

- (void)setFactor1:(NSInteger)factor1
{
    _factor1 = factor1;
    
    if (_factor1滿足條件) {
        更新約束1
    }
}

- (void)setFactor2:(NSInteger)factor2
{
    _factor2 = factor2;
    
    if (_factor2滿足條件) {
        更新約束2
    }
}

- (void)setFactor3:(NSInteger)factor3
{
    _factor3 = factor3;
    
    if (_factor3滿足條件) {
        更新約束3
    }
}

@end

優(yōu)化后:

@implementation AutoLayoutView

- (void)setFactor1:(NSInteger)factor1
{
    _factor1 = factor1;
    
    [self setNeedsUpdateConstraints];
}

- (void)setFactor2:(NSInteger)factor2
{
    _factor2 = factor2;
    
    [self setNeedsUpdateConstraints];
}

- (void)setFactor3:(NSInteger)factor3
{
    _factor3 = factor3;
    
    [self setNeedsUpdateConstraints];
}

- (void)updateConstraints
{
    if (self.factor1滿足) {
        更新約束1
    }
    
    if (self.factor2滿足) {
        更新約束2
    }
    
    if (self.factor3滿足) {
        更新約束3
    }
    
    [super updateConstraints];
}

@end

注意:一種有誤區(qū)的寫法是在- (void)updateConstraints方法中進(jìn)行初次constraint設(shè)置刺覆,這是不被推薦的严肪。推薦的寫法是在init或者viewDidLoad中進(jìn)行view的初次constraint設(shè)置。

Masonry的方法- (NSArray *)mas_updateConstraints:(void(^)(MASConstraintMaker *make))block我們?cè)诘诙?jié)已經(jīng)討論過了谦屑。剛接觸自動(dòng)布局和Masonry的同學(xué)很容易跟著感覺在- (void)updateConstraints函數(shù)里面調(diào)用Masonry的- (NSArray *)mas_updateConstraints:(void(^)(MASConstraintMaker *make))block驳糯。實(shí)際上兩者并沒有必然聯(lián)系。大多數(shù)情況在- (void)updateConstraints里面調(diào)用- (NSArray *)mas_updateConstraints:(void(^)(MASConstraintMaker *make))block很有可能產(chǎn)生布局沖突氢橙。

樣例

// 頭文件
typedef NS_ENUM(NSUInteger, AutoLayoutType) {
    HorizontalLayout,
    VerticalLayout,
};

@interface AutoLayoutView : UIView

@property (nonatomic, strong) UILabel *name;
@property (nonatomic, strong) UILabel *address;

@property (nonatomic, assign) AutoLayoutType layoutType;

@end

// 實(shí)現(xiàn)文件
@implementation AutoLayoutView

- (instancetype)initWithFrame:(CGRect)frame
{
    if (self = [super initWithFrame:frame]) {
        _name = [[UILabel alloc] init];
        [self addSubview:_name];
        
        _address = [[UILabel alloc] init];
        [self addSubview:_address];
        
        [_name mas_updateConstraints:^(MASConstraintMaker *make) {
            make.left.top.equalTo(self);
        }];
    }
    return self;
}

- (void)updateConstraints
{
    if (self.layoutType == HorizontalLayout) {
       // // 此處誤用mas_updateConstraints
        [self.address mas_updateConstraints:^(MASConstraintMaker *make) {
            make.top.equalTo(self.name);
            make.left.equalTo(self.name.mas_right).offset(10);
        }];
    } else {
        // 此處誤用mas_updateConstraints
        [self.address mas_updateConstraints:^(MASConstraintMaker *make) {
            make.left.equalTo(self.name);
            make.top.equalTo(self.name.mas_bottom).offset(10);
        }];
    }
    
    [super updateConstraints];
}

- (void)setLayoutType:(AutoLayoutType)layoutType
{
    _layoutType = layoutType;
    
    [self setNeedsUpdateConstraints];
}

@end

// 外部調(diào)用代碼
- (void)sampleCode
{
    AutoLayoutView *view = [[AutoLayoutView alloc] init];
    view.name.text = @"name";
    view.address.text = @"address";
    [self.view addSubview:view];
    [view mas_makeConstraints:^(MASConstraintMaker *make) {
        make.center.equalTo(self.view);
        make.size.mas_equalTo(CGSizeMake(200, 300));
    }];
    
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        view.layoutType = VerticalLayout; //修改布局方式后酝枢,出現(xiàn)布局沖突
    });
}

5 總結(jié)

本文梳理了一下自動(dòng)布局和Masonry使用的誤區(qū)。在基本概念沒搞清的情況下悍手,很容易犯錯(cuò)帘睦。總結(jié)起來就如下4點(diǎn):

  1. 理解自身內(nèi)容尺寸約束與抗壓抗拉
  2. NSLayoutConstraint只能修改constant和- (NSArray *)mas_updateConstraints:(void(^)(MASConstraintMaker *make))block實(shí)現(xiàn)細(xì)節(jié)之間的關(guān)系
  3. 被Masonry布局的view一定要與比較view有共同的祖先view
  4. 區(qū)分UIView的- (void)updateConstraints方法和- (NSArray *)mas_updateConstraints:(void(^)(MASConstraintMaker *make))block

6 參考資料

WWDC-Mysteries of Auto Layout, Part 2

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末坦康,一起剝皮案震驚了整個(gè)濱河市竣付,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌滞欠,老刑警劉巖古胆,帶你破解...
    沈念sama閱讀 216,744評(píng)論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異筛璧,居然都是意外死亡逸绎,警方通過查閱死者的電腦和手機(jī)妖滔,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,505評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來桶良,“玉大人座舍,你說我怎么就攤上這事≡煞” “怎么了曲秉?”我有些...
    開封第一講書人閱讀 163,105評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長疲牵。 經(jīng)常有香客問我承二,道長,這世上最難降的妖魔是什么纲爸? 我笑而不...
    開封第一講書人閱讀 58,242評(píng)論 1 292
  • 正文 為了忘掉前任亥鸠,我火速辦了婚禮,結(jié)果婚禮上识啦,老公的妹妹穿的比我還像新娘负蚊。我一直安慰自己,他們只是感情好颓哮,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,269評(píng)論 6 389
  • 文/花漫 我一把揭開白布家妆。 她就那樣靜靜地躺著,像睡著了一般冕茅。 火紅的嫁衣襯著肌膚如雪伤极。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,215評(píng)論 1 299
  • 那天姨伤,我揣著相機(jī)與錄音哨坪,去河邊找鬼。 笑死乍楚,一個(gè)胖子當(dāng)著我的面吹牛当编,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播炊豪,決...
    沈念sama閱讀 40,096評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼凌箕,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了词渤?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,939評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤串绩,失蹤者是張志新(化名)和其女友劉穎缺虐,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體礁凡,經(jīng)...
    沈念sama閱讀 45,354評(píng)論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡高氮,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,573評(píng)論 2 333
  • 正文 我和宋清朗相戀三年慧妄,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片剪芍。...
    茶點(diǎn)故事閱讀 39,745評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡塞淹,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出罪裹,到底是詐尸還是另有隱情饱普,我是刑警寧澤,帶...
    沈念sama閱讀 35,448評(píng)論 5 344
  • 正文 年R本政府宣布状共,位于F島的核電站套耕,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏峡继。R本人自食惡果不足惜冯袍,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,048評(píng)論 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望碾牌。 院中可真熱鬧康愤,春花似錦、人聲如沸舶吗。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,683評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽裤翩。三九已至资盅,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間踊赠,已是汗流浹背呵扛。 一陣腳步聲響...
    開封第一講書人閱讀 32,838評(píng)論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留筐带,地道東北人今穿。 一個(gè)月前我還...
    沈念sama閱讀 47,776評(píng)論 2 369
  • 正文 我出身青樓,卻偏偏與公主長得像伦籍,于是被迫代替她去往敵國和親蓝晒。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,652評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容