Masonry的使用

Masonry 源碼:https://github.com/Masonry/Masonryios

Masonry是一個輕量級的佈局框架 擁有本身的描述語法 採用更優雅的鏈式語法封裝自動佈局 簡潔明瞭 並具備高可讀性 並且同時支持 iOS 和 Max OS X。git

咱們先來看一段官方的sample code來認識一下Masonrygithub

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

看到block裏面的那句話: make edges equalTo superview with insets框架

經過鏈式的天然語言 就把view1給autolayout好了 是否是簡單易懂?less

使用dom

看一下Masonry支持哪一些屬性ide

@property (nonatomic, strong, readonly) MASConstraint *left;
@property (nonatomic, strong, readonly) MASConstraint *top;
@property (nonatomic, strong, readonly) MASConstraint *right;
@property (nonatomic, strong, readonly) MASConstraint *bottom;
@property (nonatomic, strong, readonly) MASConstraint *leading;
@property (nonatomic, strong, readonly) MASConstraint *trailing;
@property (nonatomic, strong, readonly) MASConstraint *width;
@property (nonatomic, strong, readonly) MASConstraint *height;
@property (nonatomic, strong, readonly) MASConstraint *centerX;
@property (nonatomic, strong, readonly) MASConstraint *centerY;
@property (nonatomic, strong, readonly) MASConstraint *baseline;

這些屬性與NSLayoutAttrubute的對照表以下函數

43.jpg

其中leading與left trailing與right 在正常狀況下是等價的 可是當一些佈局是從右至左時(好比阿拉伯文?沒有相似的經驗) 則會對調 換句話說就是基本能夠不理不用 用left和right就行了佈局

在ios8發佈後 又新增了一堆奇奇怪怪的屬性(有興趣的朋友能夠去瞅瞅) Masonry暫時還不支持(不過你要支持ios6,ios7 就不必去管那麼多了)學習

在講實例以前 先介紹一個MACRO

#define WS(weakSelf)  __weak __typeof(&*self)weakSelf = self;

快速的定義一個weakSelf 固然是用於block裏面啦 下面進入正題(爲了方便 咱們測試的superView都是一個size爲(300,300)的UIView)

下面 經過一些簡單的實例來簡單介紹如何輕鬆愉快的使用Masonry:

1. [基礎] 居中顯示一個view

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view.
     
    WS(ws);
     
    UIView *sv = [UIView new];
    [sv showPlaceHolder];
    sv.backgroundColor = [UIColor blackColor];
    [self.view addSubview:sv];
    [sv mas_makeConstraints:^(MASConstraintMaker *make) {
        make.center.equalTo(ws.view);
        make.size.mas_equalTo(CGSizeMake(300, 300));
    }];
     
}

使用我之間寫的MMPlaceHolder 能夠看到superview已經按照咱們預期居中而且設置成了適當的大小

那麼先看看這幾行代碼:

//今後之後基本能夠拋棄CGRectMake了
UIView *sv = [UIView new];
//在作autoLayout以前 必定要先將view添加到superview上 不然會報錯
[self.view addSubview:sv];
//mas_makeConstraints就是Masonry的autolayout添加函數 將所需的約束添加到block中行了
[sv mas_makeConstraints:^(MASConstraintMaker *make) {
//將sv居中(很容易理解吧?)
    make.center.equalTo(ws.view);
     
    //將size設置成(300,300)
    make.size.mas_equalTo(CGSizeMake(300, 300));
}];

這裏有兩個問題要分解一下

首先在Masonry中可以添加autolayout約束有三個函數:

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

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

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

/*

mas_makeConstraints 只負責新增約束 Autolayout不能同時存在兩條針對於同一對象的約束 不然會報錯 

mas_updateConstraints 針對上面的狀況 會更新在block中出現的約束 不會致使出現兩個相同約束的狀況

mas_remakeConstraints 則會清除以前的全部約束 僅保留最新的約束

三種函數善加利用 就能夠應對各類狀況了

*/

其次 equalTo 和 mas_equalTo的區別在哪裏呢? 其實 mas_equalTo是一個MACRO

#define mas_equalTo(...)                 equalTo(MASBoxValue((__VA_ARGS__)))
#define mas_greaterThanOrEqualTo(...)    greaterThanOrEqualTo(MASBoxValue((__VA_ARGS__)))
#define mas_lessThanOrEqualTo(...)       lessThanOrEqualTo(MASBoxValue((__VA_ARGS__)))
#define mas_offset(...)                  valueOffset(MASBoxValue((__VA_ARGS__)))

能夠看到 mas_equalTo只是對其參數進行了一個BOX操做(裝箱) MASBoxValue的定義具體能夠看看源代碼 太長就不貼出來了

所支持的類型 除了NSNumber支持的那些數值類型以外 就只支持CGPoint CGSize UIEdgeInsets

介紹完這幾個問題 咱們就繼續往下了 PS:剛纔定義的sv會成爲咱們接下來全部sample的superView

2. [初級] 讓一個view略小於其superView(邊距爲10)

UIView *sv1 = [UIView new];
[sv1 showPlaceHolder];
sv1.backgroundColor = [UIColor redColor];
[sv addSubview:sv1];
[sv1 mas_makeConstraints:^(MASConstraintMaker *make) {
    make.edges.equalTo(sv).with.insets(UIEdgeInsetsMake(10, 10, 10, 10));
     
    /* 等價於
    make.top.equalTo(sv).with.offset(10);
    make.left.equalTo(sv).with.offset(10);
    make.bottom.equalTo(sv).with.offset(-10);
    make.right.equalTo(sv).with.offset(-10);
    */
     
    /* 也等價於
    make.top.left.bottom.and.right.equalTo(sv).with.insets(UIEdgeInsetsMake(10, 10, 10, 10));
    */
}];

能夠看到 edges 其實就是top,left,bottom,right的一個簡化 分開寫也能夠 一句話更省事

那麼爲何bottom和right裏的offset是負數呢? 由於這裏計算的是絕對的數值 計算的bottom須要小魚sv的底部高度 因此要-10 同理用於right

這裏有意思的地方是and和with 其實這兩個函數什麼事情都沒作

- (MASConstraint *)with {
    return self;
}
- (MASConstraint *)and {
    return self;
}

可是用在這種鏈式語法中 就很是的巧妙和易懂 不得不佩服做者的心思

3. [初級] 讓兩個高度爲150的view垂直居中且等寬且等間隔排列 間隔爲10(自動計算其寬度)

int padding1 = 10;
[sv2 mas_makeConstraints:^(MASConstraintMaker *make) {
    make.centerY.mas_equalTo(sv.mas_centerY);
    make.left.equalTo(sv.mas_left).with.offset(padding1);
    make.right.equalTo(sv3.mas_left).with.offset(-padding1);
    make.height.mas_equalTo(@150);
    make.width.equalTo(sv3);
}];
[sv3 mas_makeConstraints:^(MASConstraintMaker *make) {
    make.centerY.mas_equalTo(sv.mas_centerY);
    make.left.equalTo(sv2.mas_right).with.offset(padding1);
    make.right.equalTo(sv.mas_right).with.offset(-padding1);
    make.height.mas_equalTo(@150);
    make.width.equalTo(sv2);
}];

這裏咱們在兩個子view之間互相設置的約束 能夠看到他們的寬度在約束下自動的被計算出來了

經過Masonry實現動畫,

UIView *redView = [[UIView alloc]init];
    redView.backgroundColor = [UIColor redColor];
    [self.view addSubview:redView];
    UIView *greenView = [[UIView alloc]init];
    greenView.backgroundColor = [UIColor greenColor];
    [self.view addSubview:greenView];
    UIView *blueView = [[UIView alloc]init];
    blueView.backgroundColor  = [UIColor blueColor];
    [self.view addSubview:blueView];

    [redView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.equalTo(self.view.mas_left).offset(20);
        make.bottom.equalTo(self.view.mas_bottom).offset(-20);
        make.width.equalTo(self.view.mas_width).multipliedBy(0.2);
        make.height.equalTo(self.view.mas_height).multipliedBy(0.2);
    }];
    [greenView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.equalTo(redView.mas_right).offset(20);
        make.bottom.equalTo(self.view.mas_bottom).offset(-20);
        make.width.equalTo(self.view.mas_width).multipliedBy(0.2);
        make.height.equalTo(self.view.mas_height).multipliedBy(0.2);
    }];
    [blueView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.equalTo(greenView.mas_right).offset(20);
        make.bottom.equalTo(self.view.mas_bottom).offset(-20);
        make.width.equalTo(self.view.mas_width).multipliedBy(0.2);
        make.height.equalTo(self.view.mas_height).multipliedBy(0.2);
        // 設置約束優先級
        make.left.equalTo(redView.mas_right).offset(20).priority(250);
    }];

動畫代碼:

[self.greenView removeFromSuperview];
    [UIView animateWithDuration:1.0f animations:^{
        [self.view layoutIfNeeded];
    }];

動畫效果請自行腦補....

mas_lessThanOrEqualTo / mas_greaterThanOrEqualTo的用法:

/**
 *  兩個label 隨長度自適應,綠色變長時,紅色的label 變短 ,可是至少留給紅色label50個點長度
 */
- (void)viewDidLoad {
    [super viewDidLoad];
    
    UILabel *greenLabel = [[UILabel alloc] init];
    greenLabel.backgroundColor = [UIColor greenColor];
    [self.view addSubview:greenLabel];
    greenLabel.text = @"123asdfasd3adsfasdfadsfsdafd3adsfasdfadsfsdaf";

    
    UILabel *redLabel = [[UILabel alloc] init];
    redLabel.backgroundColor = UIColor.redColor;
    [self.view addSubview:redLabel];
    redLabel.text = @"123asdfa2eaefqwerqwdafdafsdfadsfasdfad";
    
    [greenLabel mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.mas_equalTo(100);
        make.left.mas_equalTo(self.view).offset(10);
//        make.height.mas_equalTo(20);
        make.width.mas_lessThanOrEqualTo(self.view.frame.size.width - 50);
    }];
    
    
    [redLabel mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.mas_equalTo(greenLabel);
        make.right.equalTo(self.view);
        make.left.mas_equalTo(greenLabel.mas_right);
//        make.height.mas_equalTo(20);
    }];
}

     

4. [中級] 在UIScrollView順序排列一些view並自動計算contentSize

UIScrollView *scrollView = [UIScrollView new];
scrollView.backgroundColor = [UIColor whiteColor];
[sv addSubview:scrollView];
[scrollView mas_makeConstraints:^(MASConstraintMaker *make) {
    make.edges.equalTo(sv).with.insets(UIEdgeInsetsMake(5,5,5,5));
}];
UIView *container = [UIView new];
[scrollView addSubview:container];
[container mas_makeConstraints:^(MASConstraintMaker *make) {
    make.edges.equalTo(scrollView);
    make.width.equalTo(scrollView);
}];
int count = 10;
UIView *lastView = nil;
for ( int i = 1 ; i <= count ; ++i )
{
    UIView *subv = [UIView new];
    [container addSubview:subv];
    subv.backgroundColor = [UIColor colorWithHue:( arc4random() % 256 / 256.0 )
                                      saturation:( arc4random() % 128 / 256.0 ) + 0.5
                                      brightness:( arc4random() % 128 / 256.0 ) + 0.5
                                           alpha:1];
     
    [subv mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.and.right.equalTo(container);
        make.height.mas_equalTo(@(20*i));
         
        if ( lastView )
        {
            make.top.mas_equalTo(lastView.mas_bottom);
        }
        else
        {
            make.top.mas_equalTo(container.mas_top);
        }
    }];
     
    lastView = subv;
}
[container mas_makeConstraints:^(MASConstraintMaker *make) {
    make.bottom.equalTo(lastView.mas_bottom);
}];

頭部效果:

尾部效果:

從scrollView的scrollIndicator能夠看出 scrollView的內部已如咱們所想排列好了

這裏的關鍵就在於container這個view起到了一箇中間層的做用 可以自動的計算uiscrollView的contentSize

5. [高級] 橫向或者縱向等間隙的排列一組view

很遺憾 autoLayout並無直接提供等間隙排列的方法(Masonry的官方demo中也沒有對應的案例) 可是參考案例3 咱們能夠經過一個小技巧來實現這個目的 爲此我寫了一個Category

@implementation UIView(Masonry_LJC)
- (void) distributeSpacingHorizontallyWith:(NSArray*)views
{
    NSMutableArray *spaces = [NSMutableArray arrayWithCapacity:views.count+1];
     
    for ( int i = 0 ; i < views.count+1 ; ++i )
    {
        UIView *v = [UIView new];
        [spaces addObject:v];
        [self addSubview:v];
         
        [v mas_makeConstraints:^(MASConstraintMaker *make) {
            make.width.equalTo(v.mas_height);
        }];
    }    
     
    UIView *v0 = spaces[0];
     
    __weak __typeof(&*self)ws = self;
     
    [v0 mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.equalTo(ws.mas_left);
        make.centerY.equalTo(((UIView*)views[0]).mas_centerY);
    }];
     
    UIView *lastSpace = v0;
    for ( int i = 0 ; i < views.count; ++i )
    {
        UIView *obj = views[i];
        UIView *space = spaces[i+1];
         
        [obj mas_makeConstraints:^(MASConstraintMaker *make) {
            make.left.equalTo(lastSpace.mas_right);
        }];
         
        [space mas_makeConstraints:^(MASConstraintMaker *make) {
            make.left.equalTo(obj.mas_right);
            make.centerY.equalTo(obj.mas_centerY);
            make.width.equalTo(v0);
        }];
         
        lastSpace = space;
    }
     
    [lastSpace mas_makeConstraints:^(MASConstraintMaker *make) {
        make.right.equalTo(ws.mas_right);
    }];
     
}
- (void) distributeSpacingVerticallyWith:(NSArray*)views
{
    NSMutableArray *spaces = [NSMutableArray arrayWithCapacity:views.count+1];
     
    for ( int i = 0 ; i < views.count+1 ; ++i )
    {
        UIView *v = [UIView new];
        [spaces addObject:v];
        [self addSubview:v];
         
        [v mas_makeConstraints:^(MASConstraintMaker *make) {
            make.width.equalTo(v.mas_height);
        }];
    }
     
     
    UIView *v0 = spaces[0];
     
    __weak __typeof(&*self)ws = self;
     
    [v0 mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(ws.mas_top);
        make.centerX.equalTo(((UIView*)views[0]).mas_centerX);
    }];
     
    UIView *lastSpace = v0;
    for ( int i = 0 ; i < views.count; ++i )
    {
        UIView *obj = views[i];
        UIView *space = spaces[i+1];
         
        [obj mas_makeConstraints:^(MASConstraintMaker *make) {
            make.top.equalTo(lastSpace.mas_bottom);
        }];
         
        [space mas_makeConstraints:^(MASConstraintMaker *make) {
            make.top.equalTo(obj.mas_bottom);
            make.centerX.equalTo(obj.mas_centerX);
            make.height.equalTo(v0);
        }];
         
        lastSpace = space;
    }
     
    [lastSpace mas_makeConstraints:^(MASConstraintMaker *make) {
        make.bottom.equalTo(ws.mas_bottom);
    }];
}
@end

簡單的來測試一下

UIView *sv11 = [UIView new];
UIView *sv12 = [UIView new];
UIView *sv13 = [UIView new];
UIView *sv21 = [UIView new];
UIView *sv31 = [UIView new];
sv11.backgroundColor = [UIColor redColor];
sv12.backgroundColor = [UIColor redColor];
sv13.backgroundColor = [UIColor redColor];
sv21.backgroundColor = [UIColor redColor];
sv31.backgroundColor = [UIColor redColor];
[sv addSubview:sv11];
[sv addSubview:sv12];
[sv addSubview:sv13];
[sv addSubview:sv21];
[sv addSubview:sv31];
//給予不一樣的大小 測試效果
[sv11 mas_makeConstraints:^(MASConstraintMaker *make) {
    make.centerY.equalTo(@[sv12,sv13]);
    make.centerX.equalTo(@[sv21,sv31]);
    make.size.mas_equalTo(CGSizeMake(40, 40));
}];
[sv12 mas_makeConstraints:^(MASConstraintMaker *make) {
    make.size.mas_equalTo(CGSizeMake(70, 20));
}];
[sv13 mas_makeConstraints:^(MASConstraintMaker *make) {
    make.size.mas_equalTo(CGSizeMake(50, 50));
}];
[sv21 mas_makeConstraints:^(MASConstraintMaker *make) {
    make.size.mas_equalTo(CGSizeMake(50, 20));
}];
[sv31 mas_makeConstraints:^(MASConstraintMaker *make) {
    make.size.mas_equalTo(CGSizeMake(40, 60));
}];
[sv distributeSpacingHorizontallyWith:@[sv11,sv12,sv13]];
[sv distributeSpacingVerticallyWith:@[sv11,sv21,sv31]];
[sv showPlaceHolderWithAllSubviews];
[sv hidePlaceHolder];

perfect! 簡潔明瞭的達到了咱們所要的效果

這裏所用的技巧就是 使用空白的佔位view來填充咱們目標view的旁邊 這點經過圖上的空白標註能夠看出來

小結

經過以上5個案例 我以爲已經把Masonry的經常使用功能介紹得差很少了 若是你以爲意猶未盡呢 請下載官方的demo來學習

總而言之 Masonry是一個很是優秀的autolayout庫 可以節省大量的開發和學習時間 尤爲適合我這種純代碼的iOSer 在iPhone6發佈後引起的適配潮中 Masonry必定能夠助你一臂之力 :)

相關文章
相關標籤/搜索