從Masonry看鏈?zhǔn)骄幊?/h1>

本人有若干成套學(xué)習(xí)視頻, 可試看! 可試看! 可試看, 重要的事情說(shuō)三遍 包含Java, 數(shù)據(jù)結(jié)構(gòu)與算法, iOS, 安卓, python, flutter等等, 如有需要, 聯(lián)系微信tsaievan.

Masonry想必大家都用過(guò), 今天帶大家來(lái)稍微看一下內(nèi)部實(shí)現(xiàn)的代碼:

首先我寫(xiě)這么一段代碼:

- (void)viewDidLoad {
    [super viewDidLoad];
    UIView *yfView = [[UIView alloc] init];
    yfView.backgroundColor = [UIColor cyanColor];
    [self.view addSubview:yfView];
    
    [yfView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.left.equalTo(@50);
        make.bottom.right.equalTo(@(-50));
    }];
}

實(shí)現(xiàn)這樣一個(gè)效果, so easy對(duì)不對(duì)?

Masonry實(shí)現(xiàn)最簡(jiǎn)單效果
那么mas_makeConstraints:這個(gè)方法內(nèi)部做了些什么呢?
- (NSArray *)mas_makeConstraints:(void(^)(MASConstraintMaker *))block {
    self.translatesAutoresizingMaskIntoConstraints = NO;
    MASConstraintMaker *constraintMaker = [[MASConstraintMaker alloc] initWithView:self];
    block(constraintMaker);
    return [constraintMaker install];
}
  • 首先將translatesAutoresizingMaskIntoConstraints這個(gè)屬性改成NO, 首先, 蘋(píng)果官方是這么說(shuō)的:

By default, the autoresizing mask on a view gives rise to constraints that fully determine
the view's position. This allows the auto layout system to track the frames of views whose
layout is controlled manually (through -setFrame:, for example).
When you elect to position the view using auto layout by adding your own constraints,
you must set this property to NO. IB will do this for you.

如果你要使用自動(dòng)布局, 這個(gè)屬性就要改成NO, stroryBoard已經(jīng)幫你修改了這個(gè)屬性.

  • 創(chuàng)建一個(gè)maker對(duì)象, 丟到block里面去, 供外界使用. 所以, 大家用的make..., 就是在這個(gè)時(shí)候創(chuàng)建的.

    • 創(chuàng)建的時(shí)候, 將調(diào)用者view傳給make的屬性view
    • 同時(shí), 創(chuàng)建一個(gè)可變數(shù)組, 用來(lái)保存所有的約束對(duì)象
- (id)initWithView:(MAS_VIEW *)view {
    self = [super init];
    if (!self) return nil;
    
    self.view = view;
    self.constraints = NSMutableArray.new;
    
    return self;
}
  • 執(zhí)行block

  • 安裝約束

- (void)install {
    if (self.hasBeenInstalled) {
        return;
    }
    
    if ([self supportsActiveProperty] && self.layoutConstraint) {
        self.layoutConstraint.active = YES;
        [self.firstViewAttribute.view.mas_installedConstraints addObject:self];
        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;
    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];
    }
}

上面這一大坨惡心的代碼框架已經(jīng)幫你寫(xiě)好了, 所以我們?cè)谕饨缯{(diào)用的時(shí)候非常的方便.

但這不是今天鏈?zhǔn)骄幊痰闹攸c(diǎn), 我們需要看的是:
make.top.left.equalTo(@50);
make.bottom.right.equalTo(@(-50));

這段代碼里發(fā)生了什么?

這段代碼里頻繁使用了點(diǎn)語(yǔ)法

  • 點(diǎn)語(yǔ)法的本質(zhì)其實(shí)是get方法, (在這里可以這么理解!)
`MASConstraint`的屬性
`MASConstraint`的`get`方法
  • 在這個(gè)里面, get方法不僅僅添加了約束, 還將自身返回了出去
- (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;
}

這樣, 因?yàn)間et方法是有返回值的, 返回值又是自身, 所以可以一直點(diǎn)下去.

話說(shuō)到這里, 有一個(gè)疑問(wèn):

make.top.left.equalTo(@50)
  • 這句代碼之后, 還可以點(diǎn)語(yǔ)法點(diǎn)下去嗎? 我們來(lái)看看equalTo是個(gè)什么東東:
- (MASConstraint * (^)(id))equalTo {
    return ^id(id attribute) {
        return self.equalToWithRelation(attribute, NSLayoutRelationEqual);
    };
}

首先, equalTo依然是一個(gè)get方法, 這個(gè)get方法的返回值是一個(gè)block, 這個(gè)方法的內(nèi)部不僅僅設(shè)置約束, 還有一個(gè)返回值,返回值block直接在外界加一個(gè)括號(hào)()調(diào)用了, 調(diào)用之后, 這個(gè)block是一個(gè)參數(shù)為id類(lèi)型, 返回值為MASConstraint *類(lèi)型的, 這樣, 在調(diào)用block之后, 又得到一個(gè)MASConstraint *對(duì)象, 就又可以使用點(diǎn)語(yǔ)法, 一直點(diǎn)下去.

equalTo方法中, 返回一大段block

- (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;
        }
    };
}
下面,我們利用這種鏈?zhǔn)骄幊趟枷? 自己寫(xiě)一個(gè)加法小工具, 任何對(duì)象都可以利用這個(gè)小工具進(jìn)行計(jì)算, 代碼很簡(jiǎn)單, 關(guān)鍵是體會(huì)一下這種編程思想:
  • 首先, 我建一個(gè)分類(lèi), 在分類(lèi)中暴露這樣一個(gè)方法:
@interface NSObject (Add) 

- (int)yf_add:(void(^)(YFSumTool *tool))addBlock;

這里面我提供了一個(gè)tool, 相當(dāng)于Masonry中的make.

  • 然后提供兩個(gè)get方法
- (NSObject * (^)(int number))minus;

- (NSObject * (^)(int number))plus;
- (int)yf_add:(void(^)(YFSumTool *tool))addBlock {
    YFSumTool *tool = [[YFSumTool alloc] init];
    addBlock(tool);
    return sum;
}
  • 在這個(gè)方法中, 先創(chuàng)建一個(gè)tool對(duì)象作為參數(shù)傳到block里面去供外界使用, 然后, 調(diào)用block, 最后返回結(jié)果.

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

- (NSObject * (^)(int number))plus {
    return ^(int number) {
        sum += number;
        return self;
    };
}

- (NSObject * (^)(int number))minus {
    return ^(int number) {
        sum -= number;
        return self;
    };
}
  • get方法的返回值是block, 這個(gè)block有一個(gè)int類(lèi)型的參數(shù), 返回值為NSObject類(lèi)型, 當(dāng)這個(gè)block被調(diào)用時(shí), 將你需要加或者減的數(shù)字作為參數(shù)傳進(jìn)去, 并將自身self返回出來(lái), 這樣下次可以繼續(xù)調(diào)用, 點(diǎn)語(yǔ)法可以一直用下去:
鏈?zhǔn)骄幊?/div>

PS. 本人有若干成套學(xué)習(xí)視頻, 包含Java, 數(shù)據(jù)結(jié)構(gòu)與算法, iOS, 安卓, python, flutter等等, 如有需要, 聯(lián)系微信tsaievan.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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