Masonry解析

  • Masonry 、 AutoLayout 、 約束 、 三方庫(kù) 、 iOS


MagicNumber -> autoresizingMask -> autolayout
以上是純手寫(xiě)代碼所經(jīng)歷的關(guān)于頁(yè)面布局的三個(gè)時(shí)期

在iphone1-iphone3gs時(shí)代:window的size固定為(320,480) 。只需要簡(jiǎn)單計(jì)算一下相對(duì)位置就好了;

在iphone4-iphone4s時(shí)代:蘋(píng)果推出了retina屏 但是給了碼農(nóng)們非常大的福利:window的size不變;

在iphone5-iphone5s時(shí)代:window的size變了(320,568) 這時(shí)autoresizingMask派上了用場(chǎng)(為啥這時(shí)候不用Autolayout? 因?yàn)檫€要支持ios5唄) 簡(jiǎn)單的適配一下即可。

在iphone6+時(shí)代 window的width也發(fā)生了變化(相對(duì)5和5s的屏幕比例沒(méi)有變化) 終于是時(shí)候拋棄autoresizingMask改用autolayout了(不用支持ios5了 相對(duì)于屏幕適配的多樣性來(lái)說(shuō)autoresizingMask也已經(jīng)過(guò)時(shí)了)



AutoLayout關(guān)于更新的幾個(gè)方法的區(qū)別:

  • setNeedsLayout:告知頁(yè)面需要更新,但是不會(huì)立刻開(kāi)始更新。執(zhí)行后會(huì)立刻調(diào)用layoutSubviews。
  • layoutIfNeeded:告知頁(yè)面布局立刻更新。所以一般都會(huì)和 setNeedsLayout一起使用。如果希望立刻生成新的frame需要調(diào)用此方法,利用這點(diǎn)一般布局動(dòng)畫(huà)可以在更新布局后直接使用這個(gè)方法讓動(dòng)畫(huà)生效。
  • layoutSubviews:系統(tǒng)重寫(xiě)布局
  • setNeedsUpdateConstraints:告知需要更新約束,但是不會(huì)立刻開(kāi)始
  • updateConstraintsIfNeeded:告知立刻更新約束
  • updateConstraints:系統(tǒng)更新約束






系統(tǒng)約束

如果使用系統(tǒng)的約束,大致造型如下:

//創(chuàng)建一個(gè)圖片視圖
UIImageView* imgV = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"Oggi.png"]];
//圖片為多個(gè)狗狗 合成的不規(guī)則圖片(不規(guī)矩:非圓形、非矩形????)

imgV.backgroundColor = [UIColor redColor];//可見(jiàn)到背景色
imgV.translatesAutoresizingMaskIntoConstraints = NO;
imgV.contentMode = UIViewContentModeScaleAspectFit;
[self.view addSubview:imgV];

//imgV左側(cè)與父視圖左側(cè) 多出10個(gè)PX
NSLayoutConstraint* leftConstraint = [NSLayoutConstraint constraintWithItem:imgV attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeLeading multiplier:1.0f constant:10.0f];
//imgV右側(cè)與父視圖右側(cè)對(duì)齊
NSLayoutConstraint* rightConstraint = [NSLayoutConstraint constraintWithItem:imgV attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeTrailing multiplier:1.0f constant:0.0f];
//imgV頂部與父視圖頂部 多出20個(gè)PX
NSLayoutConstraint* topConstraint = [NSLayoutConstraint constraintWithItem:imgV attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeTop multiplier:1.0f constant:20.0f];
//imgV高度為父視圖高度一半
NSLayoutConstraint* heightConstraint = [NSLayoutConstraint constraintWithItem:imgV attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeHeight multiplier:0.5f constant:0.0f];

//iOS 6.0或者7.0調(diào)用addConstraints
//[self.view addConstraints:@[leftConstraint, rightConstraint, topConstraint, heightConstraint]];
//iOS 8.0以后設(shè)置active屬性值
leftConstraint.active = YES;
rightConstraint.active = YES;
topConstraint.active = YES;
heightConstraint.active = YES;

屬性過(guò)多,代碼冗余繁雜?。。?br> 反正我是不想用。

iPhone6s 效果圖






Xib 或者 StoryBoard </br> ??lazy but so convenient

那么還有一個(gè)最懶的方法,xib和storyBoard拖以及設(shè)置約束。但是維護(hù)起來(lái)很繁雜。牽一發(fā)而動(dòng)全身。(一點(diǎn)小失誤幾乎就得重頭再來(lái)。。。????)

storyBoard構(gòu)建約束.png

作為一名Coder,還是多敲敲代碼吧!享受指尖上的??!????






三方庫(kù)Masonry

主角 Masonry登場(chǎng)了

Masonry是一個(gè)基于AutoLayout框架的輕量級(jí)布局框架。比系統(tǒng)自帶
Masonry 源碼:https://github.com/Masonry/Masonry

Masonry支持的一些屬性:

make是MASConstraintMaker類(lèi)型。MASConstraintMaker給我們提供了22種Attribute類(lèi)型

  //Basic Attribute
  @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的對(duì)照表如下:


系統(tǒng)約束屬性


??其他屬性:

  //Margin Attribute
  @property (nonatomic, strong, readonly) MASConstraint *leftMargin;
  @property (nonatomic, strong, readonly) MASConstraint *rightMargin;
  @property (nonatomic, strong, readonly) MASConstraint *topMargin;
  @property (nonatomic, strong, readonly) MASConstraint *bottomMargin;
  @property (nonatomic, strong, readonly) MASConstraint *leadingMargin;
  @property (nonatomic, strong, readonly) MASConstraint *trailingMargin;
  @property (nonatomic, strong, readonly) MASConstraint *centerXWithinMargins;
  @property (nonatomic, strong, readonly) MASConstraint *centerYWithinMargins;


  //Convenient Attribute
  @property (nonatomic, strong, readonly) MASConstraint *edges;
  @property (nonatomic, strong, readonly) MASConstraint *size;
  @property (nonatomic, strong, readonly) MASConstraint *center;


更多細(xì)節(jié),參考:第三方 Masonry約束的使用



工程創(chuàng)建好

工程名:MasonryDemo

這里就不使用下載三方庫(kù),拖入工程的方式導(dǎo)入了。使用cocoapods在終端里導(dǎo)入Masonry 。( 記住vim Podlife前,先pod init創(chuàng)建pod文件 本人精彩犯錯(cuò)?? ?? ?? )

創(chuàng)建Podfile(配置文件)


鍵盤(pán)輸入 i,進(jìn)入編輯模式,輸入

  platform :ios, '9.0' 
  pod 'Masonry' '~> 1.4'    #'~> 1.4'版本號(hào) 根據(jù)需求可要可不要

然后按Esc,并且輸入“ :”號(hào)進(jìn)入vim命令模式,然后在冒號(hào)后邊輸入wq

pod install 成功 安裝Masonry庫(kù)
安裝Masonry庫(kù) 之后 的工程 (點(diǎn)擊 白色工程 打開(kāi)).png
打開(kāi)工程后即可看到Masonry所有東西(可謂傾家蕩產(chǎn)~~~)都放入工程里了.png


導(dǎo)入成功后即可使用Masonry約束進(jìn)行布局。

先介紹一個(gè)MACRO (因?yàn)槭褂昧薆lock,需要弱引用)

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


首先在Masonry中能夠添加autolayout約束有三個(gè)函數(shù)

  - (NSArray *)mas_makeConstraints:(void(^)(MASConstraintMaker *make))block;
  - (NSArray *)mas_updateConstraints:(void(^)(MASConstraintMaker *make))block;
  - (NSArray *)mas_remakeConstraints:(void(^)(MASConstraintMaker *make))block;
  /*
         mas_makeConstraints 只負(fù)責(zé)新增約束 Autolayout不能同時(shí)存在  兩條針對(duì)于  同一對(duì)象 的約束 否則會(huì)報(bào)錯(cuò)
         mas_updateConstraints 針對(duì)上面的情況 會(huì)  更新  在block中出現(xiàn)的約束 不會(huì)導(dǎo)致出現(xiàn)兩個(gè)相同約束的情況
         mas_remakeConstraints 則會(huì)  清除 之前的 所有約束 僅保留最新的約束
         三種函數(shù)善加利用 就可以應(yīng)對(duì)各種情況了
   */

其次 equalTo 和 mas_equalTo的區(qū)別在哪里呢? 其實(shí) mas_equalTo是一個(gè)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只是對(duì)其參數(shù)進(jìn)行了一個(gè)BOX操作(裝箱)    類(lèi)似于宏
  MASBoxValue的定義具體可看看其源代碼 

所支持的類(lèi)型 除了NSNumber支持的那些數(shù)值類(lèi)型之外
  就只支持CGPoint CGSize UIEdgeInsets

equalTo()的括號(hào)里放:視圖、mas_width等屬性、@100(對(duì)象);
mas_equalTo()的括號(hào)里放:float(基本數(shù)據(jù)類(lèi)型)。

首先導(dǎo)入庫(kù)頭文件

  #import <Masonry.h>

再寫(xiě)一些后面需要的常用定義宏

 #define WS(weakSelf)  __weak __typeof(&*self)weakSelf = self;   //block弱引用

 #define screen_W [UIScreen mainScreen].bounds.size.width       //屏寬
 #define screen_H [UIScreen mainScreen].bounds.size.height      //屏高


-(void)viewDidLoad {}里面

NSMutableArray * imgV_Arr = @[].mutableCopy;
for (int i = 0; i < 10; i ++) {  //構(gòu)建10個(gè)圖片試圖
    UIImageView * imgV = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"Oggi.png"]];
    imgV.backgroundColor = [UIColor colorWithRed:arc4random()%256/255.f green:arc4random()%256/255.f  blue:arc4random()%256/255.f  alpha:1];
  //隨機(jī)色生成

    [imgV_Arr addObject:imgV];//數(shù)組 添加圖片
  
    [self.view addSubview:imgV];
    imgV.frame = CGRectMake(i*screen_W/5.f, 0, screen_W/5.f, screen_H/10.f);
}


WS(ws);     //弱引用    __block修飾
[imgV_Arr[5] mas_makeConstraints:^(MASConstraintMaker *make) {  //第6張圖片處理
    make.center.equalTo(ws.view);             //中心 對(duì)齊
    make.size.mas_equalTo(CGSizeMake(260, 160));//尺寸
}];


[imgV_Arr[6] mas_makeConstraints:^(MASConstraintMaker *make) {  //第7張圖片處理
    make.edges.equalTo(imgV_Arr[5]).with.insets(UIEdgeInsetsMake(20, 20, 20, 20));      // 包含在內(nèi)部   內(nèi)嵌
    
    /* 等價(jià)于 */
    //  make.top.equalTo(imgV_Arr[5]).with.offset(20);
    //  make.left.equalTo(imgV_Arr[5]).with.offset(20);
    //  make.bottom.equalTo(imgV_Arr[5]).with.offset(-20);
    //  make.right.equalTo(imgV_Arr[5]).with.offset(-20);
    
    /* 也等價(jià)于 */
   //  make.top.left.bottom.and.right.equalTo(imgV_Arr[5]).with.insets(UIEdgeInsetsMake(20, 20, 20, 20));  
}];



UIView * backView = [UIView new];//純色 背景視圖
backView.backgroundColor = [UIColor whiteColor];
[self.view addSubview:backView];

[backView mas_makeConstraints:^(MASConstraintMaker *make) {
    make.left.equalTo(ws.view).with.offset(30);
    make.bottom.equalTo(ws.view).with.offset(-10);
    make.right.equalTo(ws.view).with.offset(-50);
    
    make.size.mas_equalTo(CGSizeMake(screen_W, screen_H/3.f));
}];

    UIImageView * imgV7 = imgV_Arr[7];//第8張圖片處理
    UIImageView * imgV8 = imgV_Arr[8];  //第9張圖片處理
    UIImageView * imgV9 = imgV_Arr[9];//第10張圖片處理
    //轉(zhuǎn)換為 視圖格式(??  才對(duì)應(yīng).mas_left、.mas_right屬性  ??)

    [backView addSubview: imgV7];
    [backView addSubview: imgV8];
    [backView addSubview:imgV9];

    int padding = 10;   //左右間距
    [imgV7 mas_makeConstraints:^(MASConstraintMaker *make) {
    make.centerY.mas_equalTo(backView.mas_centerY);
    make.left.equalTo(backView.mas_left).with.offset(padding);
    make.right.equalTo(imgV8.mas_left).with.offset(-padding);
    make.height.mas_equalTo(@100);//NSNumber類(lèi)型
    make.width.equalTo(imgV8);
}];
[imgV8 mas_makeConstraints:^(MASConstraintMaker *make) {
    make.centerY.mas_equalTo(backView.mas_centerY);
    make.left.equalTo(imgV7.mas_right).with.offset(padding);
    make.right.equalTo(backView.mas_right).with.offset(-padding);
    make.height.mas_equalTo(backView.mas_height);
    make.width.equalTo(imgV7);
}];


//    動(dòng)畫(huà)
[imgV9 mas_updateConstraints:^(MASConstraintMaker *make) {
    make.top.equalTo(backView).offset(30);
    make.bottom.equalTo(imgV7.mas_top).offset(-5);
    make.width.equalTo(@50);
}];
[UIView animateWithDuration:3 animations:^{ //三秒內(nèi) 播放
    [backView layoutIfNeeded];
}];
//   因?yàn)椴季旨s束就是要 脫離frame表達(dá)方式,可是動(dòng)畫(huà)是需要根據(jù)這個(gè)來(lái)執(zhí)行,就會(huì)有
 矛盾,但根據(jù)前面說(shuō)到布局約束的原理,在 某個(gè)時(shí)刻 約束也是會(huì)被 還原 成frame使視圖
顯示,這個(gè)時(shí)刻可通過(guò) layoutIfNeeded這個(gè)方法來(lái)進(jìn)行控制。

效果:

最終效果圖.png


  • 因?yàn)椴季旨s束就是要 脫離frame表達(dá)方式,可是動(dòng)畫(huà)是需要根據(jù)這個(gè)來(lái)執(zhí)行,就會(huì)有矛盾,但根據(jù)前面說(shuō)到布局約束的原理,在 某個(gè)時(shí)刻 約束也是會(huì)被 還原 成frame使視圖顯示,這個(gè)時(shí)刻可通過(guò) layoutIfNeeded這個(gè)方法來(lái)進(jìn)行控制。

    [UIView animateWithDuration:3 animations:^{ //三秒內(nèi) 播放
      [backView layoutIfNeeded];
    }];
    
下方白色視圖 Masonry動(dòng)畫(huà).gif





Masonry的屬性和類(lèi)別




后續(xù)有時(shí)間繼續(xù)更新!

后續(xù)有時(shí)間繼續(xù)更新!

后續(xù)有時(shí)間繼續(xù)更新!

之后有時(shí)間討論UIScrollView的約束建立

一直說(shuō)詳細(xì)寫(xiě)一下“UIScrollView的約束建立”,沒(méi)時(shí)間?。∽⒁獾囊稽c(diǎn)就是:UIScrollView的contentSize尺寸大?。?/strong>
1.若大于里面所有約束好的子控件的整體大小,那么直接顯示完所有的子控件;
2.若大于里面所有約束好的子控件的整體(的寬度、高度)大小,則在其寬度、高度上面把UIScrollView的contentSize拉伸開(kāi)來(lái)

??以后有空再舉了~??????
















goyohol's essay

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容