iOS自動(dòng)布局——Masonry詳解

前言

UI布局是整個(gè)前端體系里不可或缺的一環(huán)。代碼的布局是設(shè)計(jì)語(yǔ)言與用戶視覺(jué)感受溝通的橋梁橄登,不論它看起來(lái)多么簡(jiǎn)單或是瑣碎轧膘,但不得不承認(rèn),絕大部分軟件開(kāi)發(fā)的問(wèn)題舟山,都是界面問(wèn)題绸狐。那么卤恳,如何高效的完成UI開(kāi)發(fā),也是軟件行業(yè)一直在克服的問(wèn)題寒矿。

image.png

所以突琳,軟件界面開(kāi)發(fā)的核心點(diǎn)即是:如何減少UI設(shè)計(jì)稿的建模難度和減少建模轉(zhuǎn)化到代碼的實(shí)現(xiàn)難度

最初iOS提供了平面直角坐標(biāo)系的方式,來(lái)解決布局問(wèn)題符相,即所謂的手動(dòng)布局拆融。平面直角坐標(biāo)系確實(shí)是一套完備在理論,這在數(shù)學(xué)上已經(jīng)驗(yàn)證過(guò)了啊终,只要我們的屏幕還是平面镜豹,它就肯定是有效的。但有效不一定高效蓝牲,我們?cè)谌粘5纳钪刑酥苌贂?huì)用平面直角坐標(biāo)系來(lái)向人描述位置關(guān)系。更多的是依靠相對(duì)位置搞旭。

所幸散怖,iOS為我們提供自動(dòng)布局的方法,來(lái)解決這一困境肄渗。

image.png

自動(dòng)布局的基本理念

其實(shí)說(shuō)到本質(zhì)镇眷,它和手動(dòng)布局是一樣的。對(duì)一個(gè)控件放在哪里翎嫡,我們依然只關(guān)心它的(x, y, width, height)欠动。但手動(dòng)布局的方式是,一次性計(jì)算出這四個(gè)值惑申,然后設(shè)置進(jìn)去具伍,完成布局。但當(dāng)父控件或屏幕發(fā)生變化時(shí)圈驼,子控件的計(jì)算就要重新來(lái)過(guò)人芽,非常麻煩。

因此绩脆,在自動(dòng)布局中萤厅,我們不再關(guān)心(x, y, width, height)的具體值,我們只關(guān)心(x, y, width, height)四個(gè)量對(duì)應(yīng)的約束靴迫。

約束

那么何為約束呢惕味?

obj1.property1 =(obj2.property2 * multiplier)+ constant value

子控件的某一個(gè)量一定與另一個(gè)控件的某一個(gè)量呈線性關(guān)系,這就是約束玉锌。

那么名挥,給(x, y, width, height)四個(gè)量,分別給一個(gè)約束主守,就可以確定一個(gè)控件的最終位置禀倔。

    //創(chuàng)建左邊約束
    NSLayoutConstraint *leftLc = [NSLayoutConstraint constraintWithItem:blueView attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeLeft multiplier:1.0 constant:20];
    [self.view addConstraint:leftLc];

這一段代碼即是:
控件(blueView)的 x = rootView的x * 1.0 + 20
這里一定要注意榄融,這樣的一條約束,涉及了子控件和父控件蹋艺,所以這條約束一定要添加到父控件中剃袍。

添加約束的規(guī)則:

  • 如果兩個(gè)控件是父子控件,則添加到父控件中捎谨。
  • 如果兩個(gè)控件不是父子控件,則添加到層級(jí)最近的共同父控件中憔维。

示例

    //關(guān)閉Autoresizing
    blueView.translatesAutoresizingMaskIntoConstraints = NO;
    
    //創(chuàng)建左邊約束
    NSLayoutConstraint *leftLc = [NSLayoutConstraint constraintWithItem:blueView attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeLeft multiplier:1.0 constant:20];
    [self.view addConstraint:leftLc];
    
    //創(chuàng)建右邊約束
    NSLayoutConstraint *rightLc = [NSLayoutConstraint constraintWithItem:blueView attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeRight multiplier:1.0 constant:-20];
    [self.view addConstraint:rightLc];
    
    //創(chuàng)建底部約束
    NSLayoutConstraint *bottomLc = [NSLayoutConstraint constraintWithItem:blueView attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeBottom multiplier:1.0 constant:-20];
    [self.view addConstraint:bottomLc];
    
    //創(chuàng)建高度約束
    NSLayoutConstraint *heightLc = [NSLayoutConstraint constraintWithItem:blueView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:0.0 constant:50];
    [blueView addConstraint: heightLc];
效果圖

我們注意到涛救,自動(dòng)布局其實(shí)工作分兩步:

  1. 創(chuàng)建視圖的約束
  2. 將約束添加到合適的位置
    約束關(guān)系從上面的描述中,已經(jīng)非常清晰了业扒。那么如何尋找約束添加的合適位置呢检吆?

到這里,我們只是解決了如何減少UI設(shè)計(jì)稿的建模難度的問(wèn)題程储,
顯然蹭沛,減少建模轉(zhuǎn)化到代碼的實(shí)現(xiàn)難度這個(gè)效果沒(méi)能達(dá)成。關(guān)于如何解決減少建模轉(zhuǎn)化到代碼的實(shí)現(xiàn)難度的問(wèn)題章鲤,

開(kāi)源庫(kù)

上面的代碼摊灭,我們可以看到,雖然自動(dòng)布局已經(jīng)比手動(dòng)布局優(yōu)雅不少了败徊,但它依然行數(shù)較多帚呼。每條約束大約都需要三行代碼,面對(duì)復(fù)雜的頁(yè)面皱蹦,這樣開(kāi)發(fā)出來(lái)煤杀,會(huì)很難閱讀。

Masonry則為我們解決了這個(gè)問(wèn)題沪哺。

Masonry地址

引入Masonry

我們選擇使用Cocoapods的方式沈自。引入比較簡(jiǎn)單:

  1. 我們先在工程目錄下,創(chuàng)建Podfile文件:

2.編輯Podfile

其中辜妓,'IosOcDemo'就是我們工程的名字枯途,根據(jù)需要,我們自行替換嫌拣。

3.添加依賴

完成后柔袁,執(zhí)行指令pod install。CocoaPods就會(huì)為我們自動(dòng)下載并添加依賴异逐。

實(shí)踐

image.png

這樣的一個(gè)代碼捶索,用手動(dòng)布局,我們大致的代碼應(yīng)該是這樣:

-(void)initBottomView
{
    self.bottomBarView = [[UIView alloc]initWithFrame:CGRectZero];
    self.bottomButtons = [[NSMutableArray alloc]init];
    _bottomBarView.backgroundColor = [UIColor yellowColor];
    [self addSubview:_bottomBarView];
    for(int i = 0 ; i < 3 ; i++)
    {
        UIButton *button = [[UIButton alloc]initWithFrame:CGRectZero];
        button.backgroundColor = [UIColor redColor];
        [_bottomButtons addObject:button];
       [self addSubview:button];
    }
}


-(void)layoutBottomView
{
    _bottomBarView.frame = CGRectMake(20, _viewHeight - 200, _viewWidth - 40, 200);
    for (int i = 0 ; i < 3; i++) {
        UIButton *button = _bottomButtons[i];
        CGFloat x = i * (_viewWidth - 40 - 20 * 4) / 3 + 20*(i+1) + 20;
        CGFloat y = _viewHeight - 200;
        CGFloat width = (_viewWidth - 40 - 20 * 4) / 3;
        CGFloat height = 200;
        button.frame = CGRectMake(x, y, width, height);
        
    }
}

我們來(lái)看一下灰瞻,在Masonry的幫助下腥例,我們可以把剛剛的代碼寫(xiě)成什么樣的:

   -(void)initBottomView
{
        _bottomBarView = [[UIView alloc]initWithFrame:CGRectZero];
        _bottomBarView.backgroundColor = [UIColor yellowColor];
        _bottomBarView.translatesAutoresizingMaskIntoConstraints = NO;
        [self addSubview:_bottomBarView];
        [_bottomBarView mas_makeConstraints:^(MASConstraintMaker *make) {
            make.left.equalTo(self).with.offset(20);
            make.right.equalTo(self).with.offset(-20);
            make.height.mas_equalTo(200);
            make.bottom.equalTo(self);
        }];
    
    _bottomButtons = [[NSMutableArray alloc]init];
    for(int i = 0 ; i < 3 ; i++)
    {
        UIButton *button = [[UIButton alloc]initWithFrame: CGRectZero];
        button.backgroundColor = [UIColor redColor];
        button.translatesAutoresizingMaskIntoConstraints = NO;
        [_bottomButtons addObject:button];
        [_bottomBarView addSubview:button];
        [button mas_makeConstraints:^(MASConstraintMaker *make) {
            if (i == 0) {
                make.left.mas_equalTo(20);
            }else{
                UIButton *previousButton = _bottomButtons[i-1];
                make.left.equalTo(previousButton.mas_right).with.offset(20);
            }
            make.top.mas_equalTo(_bottomBarView.mas_top);
            make.width.equalTo(_bottomBarView.mas_width).with.multipliedBy(1.0f/3).offset(-20*4/3);
            make.height.equalTo(_bottomBarView.mas_height);
        }];
        
    }
}

我們可以看到在Masonry的封裝下辅甥,代碼變得非常簡(jiǎn)練易讀,需要行數(shù)略有增加燎竖,但是計(jì)算過(guò)程減少了,我們能更加關(guān)注于多個(gè)UIView間的位置關(guān)系构回,這與當(dāng)前的UI設(shè)計(jì)語(yǔ)言是契合的夏块。所以Masonry能否讓我們更直觀地表達(dá)UI。

源碼解讀

Masonry的封裝很有魅力纤掸,那么脐供,我們可以簡(jiǎn)單地來(lái)看一下,它是如何封裝的借跪。我們?cè)僮屑?xì)看一下Masonry的API會(huì)發(fā)現(xiàn)政己,我們是直接在UIView上進(jìn)行調(diào)用的。也就是說(shuō)掏愁,Masonry對(duì)UIView進(jìn)行了擴(kuò)展歇由。

View+MASUtilities.h中:

#if TARGET_OS_IPHONE || TARGET_OS_TV

    #import <UIKit/UIKit.h>
    #define MAS_VIEW UIView
    #define MAS_VIEW_CONTROLLER UIViewController
    #define MASEdgeInsets UIEdgeInsets

然后在View+MASAdditions.h中,我們看到了Masonry的擴(kuò)展:

#import "MASUtilities.h"
#import "MASConstraintMaker.h"
#import "MASViewAttribute.h"

/**
 *  Provides constraint maker block
 *  and convience methods for creating MASViewAttribute which are view + NSLayoutAttribute pairs
 */
@interface MAS_VIEW (MASAdditions)

/**
 *  following properties return a new MASViewAttribute with current view and appropriate NSLayoutAttribute
 */
@property (nonatomic, strong, readonly) MASViewAttribute *mas_left;
@property (nonatomic, strong, readonly) MASViewAttribute *mas_top;
@property (nonatomic, strong, readonly) MASViewAttribute *mas_right;
@property (nonatomic, strong, readonly) MASViewAttribute *mas_bottom;
@property (nonatomic, strong, readonly) MASViewAttribute *mas_leading;
@property (nonatomic, strong, readonly) MASViewAttribute *mas_trailing;
@property (nonatomic, strong, readonly) MASViewAttribute *mas_width;
@property (nonatomic, strong, readonly) MASViewAttribute *mas_height;
@property (nonatomic, strong, readonly) MASViewAttribute *mas_centerX;
@property (nonatomic, strong, readonly) MASViewAttribute *mas_centerY;
@property (nonatomic, strong, readonly) MASViewAttribute *mas_baseline;
@property (nonatomic, strong, readonly) MASViewAttribute *(^mas_attribute)(NSLayoutAttribute attr);

...

/**
 *  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
 */
- (NSArray *)mas_makeConstraints:(void(NS_NOESCAPE ^)(MASConstraintMaker *make))block;

一些果港,適配的代碼沦泌,我省略了,先看核心代碼京腥。在剛剛的例子中赦肃,我們正是調(diào)用的mas_makeConstraints方法。

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

mas_makeConstraints方法比較簡(jiǎn)單公浪,只是封裝了MASConstraintMaker初始化他宛,設(shè)置約束和安裝。這里的block就是我們剛剛在外層設(shè)置的約束的函數(shù)指針欠气。
也就是這一串:

^(MASConstraintMaker *make) {
        make.left.equalTo(self.view).with.offset(10);
        make.right.equalTo(self.view).with.offset(-10);
        make.height.mas_equalTo(50);
        make.bottom.equalTo(self.view).with.offset(-10);
    }

由于約束條件的設(shè)置比較復(fù)雜厅各,我們先來(lái)看看初始化和安裝。

初始化

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

初始化的代碼比較簡(jiǎn)單预柒,將傳入的view放入MASConstraintMaker成員队塘,然后創(chuàng)建MASConstraintMaker的約束容器(NSMutableArray)。

安裝

- (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;
}

安裝的代碼分為三塊:

  1. 判斷是否需要移除已有的約束宜鸯。如果需要憔古,會(huì)遍歷已有約束,然后逐個(gè)uninstall
  2. copy已有的約束淋袖,遍歷鸿市,并逐一install
  3. remove掉所有約束,并將已添加的constraints返回。

install的方法焰情,還是繼續(xù)封裝到了Constraint中陌凳,我們繼續(xù)跟進(jìn)閱讀:

我們會(huì)發(fā)現(xiàn)Constraint只是一個(gè)接口,Masonry中對(duì)于Constraint接口有兩個(gè)實(shí)現(xiàn)内舟,分別是:MASViewConstraintMASCompositeConstraint合敦。這兩個(gè)類,分別是單個(gè)約束和約束集合验游。
在上面的例子中充岛,我們只是對(duì)單個(gè)UIView進(jìn)行約束,所以我們先看MASViewConstraint的代碼批狱。
以下代碼MASViewConstraint進(jìn)行了一定程度的簡(jiǎn)化裸准,省略了一些擴(kuò)展屬性,只展示我們的例子中赔硫,會(huì)執(zhí)行的代碼:

- (void)install {
    if (self.hasBeenInstalled) {
        return;
    }
    ...
    
    MAS_VIEW *firstLayoutItem = self.firstViewAttribute.item;
    NSLayoutAttribute firstLayoutAttribute = self.firstViewAttribute.layoutAttribute;
    MAS_VIEW *secondLayoutItem = self.secondViewAttribute.item;
    NSLayoutAttribute secondLayoutAttribute = self.secondViewAttribute.layoutAttribute;

    // alignment attributes must have a secondViewAttribute
    // therefore we assume that is refering to superview
    // eg make.left.equalTo(@10)
    if (!self.firstViewAttribute.isSizeAttribute && !self.secondViewAttribute) {
        secondLayoutItem = self.firstViewAttribute.view.superview;
        secondLayoutAttribute = firstLayoutAttribute;
    }
    
    MASLayoutConstraint *layoutConstraint
        = [MASLayoutConstraint constraintWithItem:firstLayoutItem
                                        attribute:firstLayoutAttribute
                                        relatedBy:self.layoutRelation
                                           toItem:secondLayoutItem
                                        attribute:secondLayoutAttribute
                                       multiplier:self.layoutMultiplier
                                         constant:self.layoutConstant];
    
    layoutConstraint.priority = self.layoutPriority;
    layoutConstraint.mas_key = self.mas_key;
    
   if (self.secondViewAttribute.view) {
        MAS_VIEW *closestCommonSuperview = [self.firstViewAttribute.view mas_closestCommonSuperview:self.secondViewAttribute.view];
        NSAssert(closestCommonSuperview,
                 @"couldn't find a common superview for %@ and %@",
                 self.firstViewAttribute.view, self.secondViewAttribute.view);
        self.installedView = closestCommonSuperview;
    } else if (self.firstViewAttribute.isSizeAttribute) {
        self.installedView = self.firstViewAttribute.view;
    } else {
        self.installedView = self.firstViewAttribute.view.superview;
    }


    MASLayoutConstraint *existingConstraint = nil;
    ...
    else {
        [self.installedView addConstraint:layoutConstraint];
        self.layoutConstraint = layoutConstraint;
        [firstLayoutItem.mas_installedConstraints addObject:self];
    }
}

自動(dòng)布局是一種相對(duì)布局,所以盐肃,絕大部分情況下爪膊,需要兩個(gè)UIView(約束方與參照方)。在上面的方法中:

  • firstLayoutItem是約束方砸王,secondLayoutItem是參照方
  • firstLayoutAttribute是約束方的屬性推盛,secondLayoutAttribute是參照方的屬性。
  • MASLayoutConstraint就是NSLayoutConstraint的子類谦铃,只是添加了mas_key屬性耘成。
    到這里,我們就與系統(tǒng)提供的API對(duì)應(yīng)上了驹闰。
    NSLayoutConstraint *leftLc = [NSLayoutConstraint constraintWithItem:blueView attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeLeft multiplier:1.0 constant:20];
    [self.view addConstraint:leftLc];

再看看我們之前用系統(tǒng)API完成的例子瘪菌,是不是格外熟悉?

那么接下來(lái)嘹朗,我們就是要閱讀

            make.left.equalTo(self).with.offset(20);
            make.right.equalTo(self).with.offset(-20);
            make.height.mas_equalTo(200);
            make.bottom.equalTo(self);

是如何變成firstLayoutItem, secondLayoutItem, firstLayoutAttribute, secondLayoutAttributelayoutRelation的师妙。

約束條件的設(shè)置

回到前面的:

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

我們接下來(lái),就要看block的實(shí)現(xiàn):

block其實(shí)是一個(gè)函數(shù)指針屹培。此處真正調(diào)用的方法是:

            make.left.equalTo(self).with.offset(20);
            make.right.equalTo(self).with.offset(-20);
            make.height.mas_equalTo(200);
            make.bottom.equalTo(self);

我們挑選其中一個(gè)默穴,來(lái)看看源碼實(shí)現(xiàn):

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

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

- (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;
}

在對(duì)單個(gè)view添加約束時(shí),constraint為nil褪秀。我們直接生成了一個(gè)新約束newConstraint蓄诽。它的firstViewAttribute就是我們傳入的NSLayoutAttributeLeft

equalTo

- (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;
        }
    };
}

此處,我們依然先看attribute不是NSArray的情況媒吗。這里在單個(gè)屬性的約束中仑氛,就比較簡(jiǎn)單了,將relationattribue傳入MASConstraint對(duì)應(yīng)的成員蝴猪。

在上面介紹install方法時(shí)调衰,我們就曾提到過(guò):

  MASLayoutConstraint *layoutConstraint
        = [MASLayoutConstraint constraintWithItem:firstLayoutItem
                                        attribute:firstLayoutAttribute
                                        relatedBy:self.layoutRelation
                                           toItem:secondLayoutItem
                                        attribute:secondLayoutAttribute
                                       multiplier:self.layoutMultiplier
                                         constant:self.layoutConstant];

firstLayoutItemsecondLayoutIteminstall方法中已收集完成膊爪,此時(shí),經(jīng)過(guò)leftequalTo我們又收集到了:firstViewAttribute嚎莉、secondViewAttributelayoutRelation勝利即在眼前米酬。

- (MASConstraint * (^)(CGFloat))offset {
    return ^id(CGFloat offset){
        self.offset = offset;
        return self;
    };
}

- (void)setOffset:(CGFloat)offset {
    self.layoutConstant = offset;
}

通過(guò)OC的set語(yǔ)法,Masonryoffset傳入layoutConstant趋箩。

至此赃额,layoutConstraint就完成了全部的元素收集,可以使用添加約束的方式叫确,只需要解決最后一個(gè)問(wèn)題跳芳,約束添加到哪里呢?我們似乎在調(diào)用時(shí)竹勉,并不需要關(guān)心這件事情飞盆,那說(shuō)明框架幫我們完成了這個(gè)工作。

closestCommonSuperview

我們?cè)贛ASViewConstraint中次乓,可以找到這樣一段:

    if (self.secondViewAttribute.view) {
        MAS_VIEW *closestCommonSuperview = [self.firstViewAttribute.view mas_closestCommonSuperview:self.secondViewAttribute.view];
        NSAssert(closestCommonSuperview,
                 @"couldn't find a common superview for %@ and %@",
                 self.firstViewAttribute.view, self.secondViewAttribute.view);
        self.installedView = closestCommonSuperview;
    } else if (self.firstViewAttribute.isSizeAttribute) {
        self.installedView = self.firstViewAttribute.view;
    } else {
        self.installedView = self.firstViewAttribute.view.superview;
    }

注意到吓歇,closetCommonSuperview就是Masonry為我們找到的最近公共父控件。

- (instancetype)mas_closestCommonSuperview:(MAS_VIEW *)view {
    MAS_VIEW *closestCommonSuperview = nil;

    MAS_VIEW *secondViewSuperview = view;
    while (!closestCommonSuperview && secondViewSuperview) {
        MAS_VIEW *firstViewSuperview = self;
        while (!closestCommonSuperview && firstViewSuperview) {
            if (secondViewSuperview == firstViewSuperview) {
                closestCommonSuperview = secondViewSuperview;
            }
            firstViewSuperview = firstViewSuperview.superview;
        }
        secondViewSuperview = secondViewSuperview.superview;
    }
    return closestCommonSuperview;
}

實(shí)現(xiàn)也比較簡(jiǎn)單票腰。

至此城看,我們完成了所有準(zhǔn)備,就可以開(kāi)始愉快的自動(dòng)布局啦杏慰。

以上就是Masonry對(duì)iOS自動(dòng)布局封裝的解讀测柠。

如有問(wèn)題,歡迎指正缘滥。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末轰胁,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子完域,更是在濱河造成了極大的恐慌软吐,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,204評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件吟税,死亡現(xiàn)場(chǎng)離奇詭異凹耙,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)肠仪,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門肖抱,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人异旧,你說(shuō)我怎么就攤上這事意述。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,548評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵荤崇,是天一觀的道長(zhǎng)拌屏。 經(jīng)常有香客問(wèn)我,道長(zhǎng)术荤,這世上最難降的妖魔是什么倚喂? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,657評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮瓣戚,結(jié)果婚禮上端圈,老公的妹妹穿的比我還像新娘。我一直安慰自己子库,他們只是感情好舱权,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,689評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著仑嗅,像睡著了一般宴倍。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上仓技,一...
    開(kāi)封第一講書(shū)人閱讀 51,554評(píng)論 1 305
  • 那天啊楚,我揣著相機(jī)與錄音,去河邊找鬼浑彰。 笑死,一個(gè)胖子當(dāng)著我的面吹牛拯辙,可吹牛的內(nèi)容都是我干的郭变。 我是一名探鬼主播,決...
    沈念sama閱讀 40,302評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼涯保,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼诉濒!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起夕春,我...
    開(kāi)封第一講書(shū)人閱讀 39,216評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤未荒,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后及志,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體片排,經(jīng)...
    沈念sama閱讀 45,661評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,851評(píng)論 3 336
  • 正文 我和宋清朗相戀三年速侈,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了率寡。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,977評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡倚搬,死狀恐怖冶共,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤捅僵,帶...
    沈念sama閱讀 35,697評(píng)論 5 347
  • 正文 年R本政府宣布家卖,位于F島的核電站,受9級(jí)特大地震影響庙楚,放射性物質(zhì)發(fā)生泄漏上荡。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,306評(píng)論 3 330
  • 文/蒙蒙 一醋奠、第九天 我趴在偏房一處隱蔽的房頂上張望榛臼。 院中可真熱鬧,春花似錦窜司、人聲如沸沛善。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,898評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)金刁。三九已至,卻和暖如春议薪,著一層夾襖步出監(jiān)牢的瞬間尤蛮,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,019評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工斯议, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留产捞,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,138評(píng)論 3 370
  • 正文 我出身青樓哼御,卻偏偏與公主長(zhǎng)得像坯临,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子恋昼,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,927評(píng)論 2 355

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