Masonry是如何適配iOS11的

iOS 源代碼分析 --- Masonry

Masonry 是 Objective-C 中用于自動(dòng)布局的第三方框架, 我們一般使用它來(lái)代替冗長(zhǎng), 繁瑣的 AutoLayout 代碼.

Masonry 的使用還是很簡(jiǎn)潔的:

[button mas_makeConstraints:^(MASConstraintMaker *make) {
    make.centerX.equalTo(self.view);
    make.top.equalTo(self.view).with.offset(40);
    make.width.equalTo(@185);
    make.height.equalTo(@38);
}];

從 mas_makeConstraints: 開始

其中最常用的方法就是

// View+MASAdditions.h

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

同樣, 也有用于更新和重新構(gòu)建約束的分類方法:

// View+MASAdditions.h

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

Constraint Maker Block

我們以 mas_makeConstraints: 方法為入口來(lái)分析一下 Masonry 以及類似的框架(SnapKit)是如何工作的. mas_makeConstraints: 方法位于 UIView 的分類 MASAdditions 中.

Provides constraint maker block and convience methods for creating MASViewAttribute which are view + NSLayoutAttribute pairs.

這個(gè)分類為我們提供一種非常便捷的方法來(lái)配置 MASConstraintMaker, 并為視圖添加 mas_left mas_right 等屬性.

方法的實(shí)現(xiàn)如下:

// View+MASAdditions.m

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

因?yàn)?Masonry 是封裝的蘋果的 AutoLayout 框架, 所以我們要在為視圖添加約束前將 translatesAutoresizingMaskIntoConstraints 屬性設(shè)置為 NO. 如果這個(gè)屬性沒(méi)有被正確設(shè)置, 那么視圖的約束不會(huì)被成功添加纽疟。
translatesAutoresizingMaskIntoConstraints

A Boolean value that determines whether the view’s autoresizing mask is translated into Auto Layout constraints.

該屬性決定是否關(guān)閉autiresizing中贝,當(dāng)UIView的autoresizesSubviews是YES時(shí)倔丈,(默認(rèn)是YES), 那么在其中的子view會(huì)根據(jù)它自身的autoresizingMask屬性來(lái)自動(dòng)適應(yīng)其與superView之間的位置和大小伦籍。
autoresizingMask是一個(gè)枚舉類型, 默認(rèn)是UIViewAutoresizingNone, 也就是不會(huì)autoresize:

typedef NS_OPTIONS(NSUInteger, UIViewAutoresizing) {
    UIViewAutoresizingNone                 = 0,
    UIViewAutoresizingFlexibleLeftMargin   = 1 << 0,
    UIViewAutoresizingFlexibleWidth        = 1 << 1,
    UIViewAutoresizingFlexibleRightMargin  = 1 << 2,
    UIViewAutoresizingFlexibleTopMargin    = 1 << 3,
    UIViewAutoresizingFlexibleHeight       = 1 << 4,
    UIViewAutoresizingFlexibleBottomMargin = 1 << 5
};

這個(gè)枚舉類型皆串,使用了 1 << n 這樣的寫法來(lái)定義,代表了它可以復(fù)選埃仪。在一個(gè)xib文件中沫屡,取消勾選autolayout拌牲,(默認(rèn)使用autolayout時(shí)俱饿,autoresizing看不到)歌粥。那么我們可以在布局那一欄看到如何設(shè)置autoresizing.

在設(shè)置 translatesAutoresizingMaskIntoConstraints 屬性為NO之后,

  • 我們會(huì)初始化一個(gè) MASConstraintMaker 的實(shí)例.
  • 然后將 maker 傳入 block 配置其屬性.
  • 最后調(diào)用 maker 的 install 方法為視圖添加約束.

MASConstraintMaker

MASConstraintMaker 為我們提供了工廠方法來(lái)創(chuàng)建 MASConstraint. 所有的約束都會(huì)被收集直到它們最后調(diào)用 install 方法添加到視圖上.

Provides factory methods for creating MASConstraints. Constraints are collected until they are ready to be installed

在初始化 MASConstraintMaker 的實(shí)例時(shí), 它會(huì)持有一個(gè)對(duì)應(yīng) view 的弱引用, 并初始化一個(gè) constraints 的空可變數(shù)組用來(lái)之后配置屬性時(shí)持有所有的約束.

// MASConstraintMaker.m

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

這里的 MAS_VIEW 是一個(gè)宏, 是 UIView 的 alias(別名).

// MASUtilities.h

#define MAS_VIEW UIView

Setup MASConstraintMaker

在調(diào)用 block(constraintMaker) 時(shí), 實(shí)際上是對(duì) constraintMaker 的配置.

make.centerX.equalTo(self.view);
make.top.equalTo(self.view).with.offset(40);
make.width.equalTo(@185);
make.height.equalTo(@38);

make.left

訪問(wèn) makeleft right top bottom 等屬性時(shí), 會(huì)調(diào)用 constraint:addConstraintWithLayoutAttribute: 方法.

// MASViewConstraint.m

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

- (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]) { ... }
    if (!constraint) {
        newConstraint.delegate = self;
        [self.constraints addObject:newConstraint];
    }
    return newConstraint;
}

在調(diào)用鏈上最終會(huì)達(dá)到 constraint:addConstraintWithLayoutAttribute: 這一方法, 在這里省略了一些暫時(shí)不需要了解的問(wèn)題. 因?yàn)樵谶@個(gè)類中傳入該方法的第一個(gè)參數(shù)一直為 nil, 所以這里省略的代碼不會(huì)執(zhí)行塌忽。

這部分代碼會(huì)先以布局屬性 left 和視圖本身初始化一個(gè) MASViewAttribute 的實(shí)例, 之后使用 MASViewAttribute 的實(shí)例初始化一個(gè) constraint 并設(shè)置它的代理, 加入數(shù)組, 然后返回。

這些工作就是你在輸入 make.left 進(jìn)行的全部工作, 它會(huì)返回一個(gè) MASConstraint, 用于之后的繼續(xù)配置失驶。

make.left.equalTo(@80)

make.left 返回 MASConstraint 之后, 我們會(huì)繼續(xù)在這個(gè)鏈?zhǔn)降恼Z(yǔ)法中調(diào)用下一個(gè)方法來(lái)指定約束的關(guān)系.

// MASConstraint.h

- (MASConstraint * (^)(id attr))equalTo;
- (MASConstraint * (^)(id attr))greaterThanOrEqualTo;
- (MASConstraint * (^)(id attr))lessThanOrEqualTo;

這三個(gè)方法是在 MASViewConstraint 的父類, MASConstraint 中定義的.

MASConstraint 是一個(gè)抽象類, 其中有很多的方法都必須在子類中覆寫的. Masonry 中有兩個(gè) MASConstraint 的子類, 分別是 MASViewConstraintMASCompositeConstraint. 后者實(shí)際上是一些約束的集合. 這么設(shè)計(jì)的原因我們會(huì)在 post 的最后解釋.

先來(lái)看一下這三個(gè)方法是怎么實(shí)現(xiàn)的:

// MASConstraint.m

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

該方法會(huì)導(dǎo)致 self.equalToWithRelation 的執(zhí)行, 而這個(gè)方法是定義在子類中的, 因?yàn)楦割愖鳛槌橄箢悰](méi)有提供這個(gè)方法的具體實(shí)現(xiàn).

// MASConstraint.m

- (MASConstraint * (^)(id, NSLayoutRelation))equalToWithRelation { MASMethodNotImplemented(); }

MASMethodNotImplemented 也是一個(gè)宏定義, 用于在子類未繼承這個(gè)方法或者直接使用這個(gè)類時(shí)拋出異常.

// MASConstraint.m

#define MASMethodNotImplemented() \
    @throw [NSException exceptionWithName:NSInternalInconsistencyException \
                                   reason:[NSString stringWithFormat:@"You must override %@ in a subclass.", NSStringFromSelector(_cmd)] \
                                 userInfo:nil]

因?yàn)槲覀優(yōu)?equalTo 提供了參數(shù) attribute 和布局關(guān)系 NSLayoutRelationEqual, 這兩個(gè)參數(shù)會(huì)傳遞到 equalToWithRelation 中, 設(shè)置 constraint 的布局關(guān)系和 secondViewAttribute 屬性, 為即將 maker 的 install 做準(zhǔn)備.

// MASViewConstraint.m

- (MASConstraint * (^)(id, NSLayoutRelation))equalToWithRelation {
    return ^id(id attribute, NSLayoutRelation relation) {
        if ([attribute isKindOfClass:NSArray.class]) { ... } 
        else {
            ...
            self.layoutRelation = relation;
            self.secondViewAttribute = attribute;
            return self;
        }
    };
}

我們不得不提一下 setSecondViewAttribute: 方法, 它并不只是一個(gè)簡(jiǎn)單的 setter 方法, 它會(huì)根據(jù)你傳入的值的種類賦值.

// MASConstraintMaker.m

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

第一種情況對(duì)應(yīng)的就是:

make.left.equalTo(@40);

傳入 NSValue 的時(shí), 會(huì)直接設(shè)置 constraintoffset, centerOffset, sizeOffset, 或者 insets

第二種情況一般會(huì)直接傳入一個(gè)視圖:

make.left.equalTo(view);

這時(shí), 就會(huì)初始化一個(gè) layoutAttribute 屬性與 firstViewArribute 相同的 MASViewAttribute, 上面的代碼就會(huì)使視圖與 view 左對(duì)齊.

第三種情況會(huì)傳入一個(gè)視圖的 MASViewAttribute:

make.left.equalTo(view.mas_right);

使用這種寫法時(shí), 一般是因?yàn)榧s束的方向不同. 這行代碼會(huì)使視圖的左側(cè)與 view 的右側(cè)對(duì)齊.

到這里我們就基本完成了對(duì)一個(gè)約束的配置, 接下來(lái)可以使用相同的語(yǔ)法完成對(duì)一個(gè)視圖上所有約束進(jìn)行配置, 然后進(jìn)入了最后一個(gè)環(huán)節(jié).

Install MASConstraintMaker

我們會(huì)在 mas_makeConstraints: 方法的最后調(diào)用 [constraintMaker install] 方法來(lái)安裝所有存儲(chǔ)在 self.constraints 數(shù)組中的所有約束.

// MASConstraintMaker.m

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

在這個(gè)方法會(huì)先判斷當(dāng)前的視圖的約束是否應(yīng)該要被 uninstall, 如果我們?cè)谧铋_始調(diào)用 mas_remakeConstraints: 方法時(shí), 視圖中原來(lái)的約束就會(huì)全部被 uninstall.

然后就會(huì)遍歷 constraints 數(shù)組, 發(fā)送 install 消息.

MASViewConstraint install

MASViewConstraint 的 install 方法就是最后為當(dāng)前視圖添加約束的最后的方法, 首先這個(gè)方法會(huì)先獲取即將用于初始化 NSLayoutConstraint 的子類的幾個(gè)屬性.

// MASViewConstraint.m

MAS_VIEW *firstLayoutItem = self.firstViewAttribute.view;
NSLayoutAttribute firstLayoutAttribute = self.firstViewAttribute.layoutAttribute;
MAS_VIEW *secondLayoutItem = self.secondViewAttribute.view;
NSLayoutAttribute secondLayoutAttribute = self.secondViewAttribute.layoutAttribute;

Masonry 之后會(huì)判斷當(dāng)前即將添加的約束是否是 size 類型的約束

// MASViewConstraint.m

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

如果不是 size 類型并且沒(méi)有提供第二個(gè) viewAttribute, (e.g. make.left.equalTo(@10);) 會(huì)自動(dòng)將約束添加到 superview 上. 它等價(jià)于:

make.left.equalTo(superView.mas_left).with.offset(10);

然后就會(huì)初始化 NSLayoutConstraint 的子類 MASLayoutConstraint:

// MASViewConstraint.m

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

接下來(lái)它會(huì)尋找 firstLayoutItemsecondLayoutItem 兩個(gè)視圖的公共 superview, 相當(dāng)于求兩個(gè)數(shù)的最小公倍數(shù).

// View+MASAdditions.m

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

如果需要升級(jí)當(dāng)前的約束就會(huì)獲取原有的約束, 并替換為新的約束, 這樣就不需要再次為 view 安裝約束.

// MASViewConstraint.m

MASLayoutConstraint *existingConstraint = nil;
if (self.updateExisting) {
   existingConstraint = [self layoutConstraintSimilarTo:layoutConstraint];
}
if (existingConstraint) {
   // just update the constant
   existingConstraint.constant = layoutConstraint.constant;
   self.layoutConstraint = existingConstraint;
} else {
   [self.installedView addConstraint:layoutConstraint];
   self.layoutConstraint = layoutConstraint;
}
    
[firstLayoutItem.mas_installedConstraints addObject:self];

如果原來(lái)的 view 中不存在可以升級(jí)的約束, 或者沒(méi)有調(diào)用 mas_updateConstraint: 方法, 那么就會(huì)在上一步尋找到的 installedView 上面添加約束.

[self.installedView addConstraint:layoutConstraint];

其他問(wèn)題

到現(xiàn)在為止整個(gè) Masonry 為視圖添加約束的過(guò)程就已經(jīng)完成了, 然而我們還有一些待解決的其它問(wèn)題.

make.left.equal(view).with.offset(30)

我們?cè)谇懊娴挠懻撝幸呀?jīng)討論了這個(gè)鏈?zhǔn)秸Z(yǔ)法的前半部分, 但是在使用中也會(huì)"延長(zhǎng)"這個(gè)鏈?zhǔn)秸Z(yǔ)句, 比如添加 with offset.

其實(shí)在 Masonry 中使用 with 并不是必須的, 它的作用僅僅是使代碼更加的易讀.

Optional semantic property which has no effect but improves the readability of constraint

// MASConstraint.m
- (MASConstraint *)with {
    return self;
}

- (MASConstraint *)and {
    return self;
}

with 有著相同作用的還有 and, 這兩個(gè)方法都會(huì)直接返回 MASConstraint, 方法本身不做任何的修改.

offset 方法其實(shí)是修改 layoutConstraint 中的常量, 因?yàn)?self.layoutConstant 在初始化時(shí)會(huì)被設(shè)置為 0, 我們可以通過(guò)修改 offset 屬性來(lái)改變它.

// MASViewConstraint.m

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

MASCompositeConstraint

MASCompositeConstraint 是一些 MASConstraint 的集合, 它能夠提供一種更加便捷的方法同時(shí)為一個(gè)視圖來(lái)添加多個(gè)約束.

A group of MASConstraint objects

通過(guò) make 直接調(diào)用 edges size center 時(shí), 就會(huì)產(chǎn)生一個(gè) MASCompositeConstraint 的實(shí)例, 而這個(gè)實(shí)例會(huì)初始化所有對(duì)應(yīng)的單獨(dú)的約束.

// MASConstraintMaker.m

- (MASConstraint *)edges {
    return [self addConstraintWithAttributes:MASAttributeTop | MASAttributeLeft | MASAttributeRight | MASAttributeBottom];
}

- (MASConstraint *)size {
    return [self addConstraintWithAttributes:MASAttributeWidth | MASAttributeHeight];
}

- (MASConstraint *)center {
    return [self addConstraintWithAttributes:MASAttributeCenterX | MASAttributeCenterY];
}

這些屬性都會(huì)調(diào)用 addConstraintWithAttributes: 方法, 生成多個(gè)屬于 MASCompositeConstraint 的實(shí)例.

// MASConstraintMaker.m

NSMutableArray *children = [NSMutableArray arrayWithCapacity:attributes.count];
    
for (MASViewAttribute *a in attributes) {
   [children addObject:[[MASViewConstraint alloc] initWithFirstViewAttribute:a]];
}
    
MASCompositeConstraint *constraint = [[MASCompositeConstraint alloc] initWithChildren:children];
constraint.delegate = self;
[self.constraints addObject:constraint];
return constraint;

mas_equalTo

Masonry 中還有一個(gè)類似與 magic 的宏, 這個(gè)宏將 C 和 Objective-C 語(yǔ)言中的一些基本數(shù)據(jù)結(jié)構(gòu)比如說(shuō) double CGPoint CGSize 這些值用 NSValue 進(jìn)行包裝.

這是一種非常簡(jiǎn)潔的使用方式, 如果你對(duì)這個(gè)非常感興趣, 可以看一下 MASUtilities.h 中的源代碼, 在這里就不對(duì)這個(gè)做出解釋了.

Masonry 如何為視圖添加約束的土居,可以總結(jié)為以下幾句。

Masonry 與其它的第三方開源框架一樣選擇了使用分類的方式為 UIKit 添加一個(gè)方法 mas_makeConstraint, 這個(gè)方法接受了一個(gè) block, 這個(gè) block 有一個(gè) MASConstraintMaker 類型的參數(shù), 這個(gè) maker 會(huì)持有一個(gè)約束的數(shù)組, 這里保存著所有將被加入到視圖中的約束.

我們通過(guò)鏈?zhǔn)降恼Z(yǔ)法配置 maker, 設(shè)置它的 left right 等屬性, 比如說(shuō) make.left.equalTo(view), 其實(shí)這個(gè) left equalTo 還有像 with offset 之類的方法都會(huì)返回一個(gè) MASConstraint 的實(shí)例, 所以在這里才可以用類似 Ruby 中鏈?zhǔn)降恼Z(yǔ)法.

在配置結(jié)束后, 首先會(huì)調(diào)用 maker 的 install 方法, 而這個(gè) maker 的 install 方法會(huì)遍歷其持有的約束數(shù)組, 對(duì)其中的每一個(gè)約束發(fā)送 install 消息. 在這里就會(huì)使用到在上一步中配置的屬性, 初始化 NSLayoutConstraint 的子類 MASLayoutConstraint 并添加到合適的視圖上.

視圖的選擇會(huì)通過(guò)調(diào)用一個(gè)方法 mas_closestCommonSuperview: 來(lái)返回兩個(gè)視圖的最近公共父視圖.

Masonry是如何適配iOS11的

在iOS11中新增了安全區(qū)域(Safe Area)嬉探,可點(diǎn)擊查看iOS11新特性擦耀。
在Masonry更新中,作者在Demo工程中新增了MASExampleSafeAreaLayoutGuideViewController

IMG_F2D07D31CAE9-1.jpeg
涩堤。LT眷蜓、RT、LB胎围、RB構(gòu)成的區(qū)域即為安全域吁系,對(duì)于安全域的約束,可以參看以下代碼

[view1 mas_makeConstraints:^(MASConstraintMaker *make) {
     make.edges.equalTo(self.view.mas_safeAreaLayoutGuide).inset(10.0);
}];
......
[rightTopView mas_makeConstraints:^(MASConstraintMaker *make) {
     make.right.equalTo(self.view.mas_safeAreaLayoutGuideRight);
     make.top.equalTo(self.view.mas_safeAreaLayoutGuideTop);
     make.width.height.equalTo(@(size));
}];

make.edges.equalTo(self.view.mas_safeAreaLayoutGuide).inset(10.0);是在安全域內(nèi)白魂,向內(nèi)縮進(jìn)10汽纤。
mas_safeAreaLayoutGuide作為屬性在調(diào)用時(shí),實(shí)際上是執(zhí)行以下代碼

- (MASViewAttribute *)mas_safeAreaLayoutGuide {
    return [[MASViewAttribute alloc] initWithView:self item:self.safeAreaLayoutGuide layoutAttribute:NSLayoutAttributeNotAnAttribute];
}

initWithView: item是為了適配iOS11新增的初始化方法福荸,在iOS11之前item是默認(rèn)為initWithView的參數(shù)蕴坪,也就是這里的self。
safeAreaLayoutGuide屬性是iOS11新增的敬锐,蘋果文檔對(duì)它的解釋是

When the view is visible onscreen, this guide reflects the portion of the view that is not covered by navigation bars, tab bars, toolbars, and other ancestor views. (In tvOS, the safe area reflects the area not covered the screen's bezel.) If the view is not currently installed in a view hierarchy, or is not yet visible onscreen, the layout guide edges are equal to the edges of the view.
For the view controller's root view, the layout guide accommodates the status bar, other visible bars, and any additional insets that you specified using the additionalSafeAreaInsets
property of your view controller. For other views in the view hierarchy, the layout guide reflects only the portion of the view that is covered by other content. For example, if a view is entirely within the safe area of its superview, the layout guide edges are equal to the edges of the view.

當(dāng)視圖在屏幕上可見(jiàn)時(shí)背传,safeAreaLayoutGuide反映了navigation bars,tab bars, toolbars和其他根視圖所沒(méi)有覆蓋的視圖的一部分。(在tvOS台夺,安全區(qū)域指未覆蓋屏幕邊框的可見(jiàn)區(qū)域径玖。)如果視圖當(dāng)前沒(méi)有添加到視圖層次結(jié)構(gòu)中,或者在屏幕上還沒(méi)有顯示谒养,那么safeAreaLayoutGuide邊緣就等于視圖的邊緣挺狰。
對(duì)于視圖控制器的根視圖,safeAreaLayoutGuide可容納 status bar, other visible bars以及使用additionalsafeareainset指定的任何附加的insets买窟。對(duì)于視圖層次結(jié)構(gòu)中的其他視圖丰泊,safeAreaLayoutGuide和父視圖具有相同的安全域。例如始绍,如果一個(gè)視圖的父視圖在安全區(qū)域內(nèi)瞳购,那么safeAreaLayoutGuide的邊緣就等于視圖的邊緣。

小結(jié)

雖然 Masonry 這個(gè)框架中的代碼并不是非常的多, 只有 1,2 萬(wàn)行的代碼, 但是感覺(jué)這個(gè)項(xiàng)目閱讀起來(lái)十分的困難, 沒(méi)有 SDWebImage 清晰, 因?yàn)榇a中類的屬性非常的多, 而且有很多相似的屬性會(huì)干擾我們對(duì)這個(gè)項(xiàng)目的閱讀, 整個(gè)框架運(yùn)用了大量的 block 語(yǔ)法進(jìn)行回調(diào).

雖然代碼十分整潔不過(guò)我覺(jué)得卻降低了可讀性, 但是還是那句話, 把簡(jiǎn)潔留給別人復(fù)雜留給自己, 只要為開發(fā)者提供簡(jiǎn)潔的接口就可以了亏推。

感謝:
譯者: @One @Draveness

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末学赛,一起剝皮案震驚了整個(gè)濱河市年堆,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌盏浇,老刑警劉巖变丧,帶你破解...
    沈念sama閱讀 216,651評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異绢掰,居然都是意外死亡痒蓬,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,468評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門滴劲,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)攻晒,“玉大人,你說(shuō)我怎么就攤上這事班挖÷衬螅” “怎么了?”我有些...
    開封第一講書人閱讀 162,931評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵萧芙,是天一觀的道長(zhǎng)给梅。 經(jīng)常有香客問(wèn)我,道長(zhǎng)末购,這世上最難降的妖魔是什么破喻? 我笑而不...
    開封第一講書人閱讀 58,218評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮盟榴,結(jié)果婚禮上曹质,老公的妹妹穿的比我還像新娘。我一直安慰自己擎场,他們只是感情好羽德,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,234評(píng)論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著迅办,像睡著了一般宅静。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上站欺,一...
    開封第一講書人閱讀 51,198評(píng)論 1 299
  • 那天姨夹,我揣著相機(jī)與錄音,去河邊找鬼矾策。 笑死磷账,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的贾虽。 我是一名探鬼主播逃糟,決...
    沈念sama閱讀 40,084評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了绰咽?” 一聲冷哼從身側(cè)響起菇肃,我...
    開封第一講書人閱讀 38,926評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎取募,沒(méi)想到半個(gè)月后琐谤,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,341評(píng)論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡矛辕,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,563評(píng)論 2 333
  • 正文 我和宋清朗相戀三年笑跛,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了付魔。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片聊品。...
    茶點(diǎn)故事閱讀 39,731評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖几苍,靈堂內(nèi)的尸體忽然破棺而出翻屈,到底是詐尸還是另有隱情,我是刑警寧澤妻坝,帶...
    沈念sama閱讀 35,430評(píng)論 5 343
  • 正文 年R本政府宣布伸眶,位于F島的核電站,受9級(jí)特大地震影響刽宪,放射性物質(zhì)發(fā)生泄漏厘贼。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,036評(píng)論 3 326
  • 文/蒙蒙 一圣拄、第九天 我趴在偏房一處隱蔽的房頂上張望嘴秸。 院中可真熱鬧,春花似錦庇谆、人聲如沸岳掐。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,676評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)串述。三九已至,卻和暖如春寞肖,著一層夾襖步出監(jiān)牢的瞬間纲酗,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,829評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工新蟆, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留觅赊,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,743評(píng)論 2 368
  • 正文 我出身青樓栅葡,卻偏偏與公主長(zhǎng)得像茉兰,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子欣簇,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,629評(píng)論 2 354

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

  • Masonry是iOS在控件布局中經(jīng)常使用的一個(gè)輕量級(jí)框架规脸,Masonry讓NSLayoutConstraint使...
    丘比沙拉閱讀 3,181評(píng)論 2 19
  • Masonry是iOS在控件布局中經(jīng)常使用的一個(gè)輕量級(jí)框架坯约,Masonry讓NSLayoutConstraint使...
    愛(ài)敲代碼的果果閱讀 1,880評(píng)論 0 2
  • 我們先來(lái)看看是如何開始使用Masonry的,一般我們使用這個(gè)布局框架的時(shí)候莫鸭,都會(huì)調(diào)用以下代碼闹丐。。被因。卿拴。。 [self...
    smile小芳閱讀 1,157評(píng)論 0 0
  • Masonry是一個(gè)輕量級(jí)的布局框架梨与,擁有自己的描述語(yǔ)法堕花,采用更優(yōu)雅的鏈?zhǔn)秸Z(yǔ)法封裝自動(dòng)布局,簡(jiǎn)潔明了并具有高可讀性...
    3dcc6cf93bb5閱讀 1,765評(píng)論 0 1
  • 生命本輕如浮萍粥鞋,拼得半生圖虛名缘挽。 一朝秋雨春難在,何故曲意論輸贏呻粹。 渾噩蹉跎三十載壕曼,如夢(mèng)初醒真理明。 至是無(wú)為一身...
    你好小土豆閱讀 259評(píng)論 0 0