Masonry解析

  • Masonry 、 AutoLayout 、 約束 、 三方庫 、 iOS


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

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

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

在iphone5-iphone5s時代:window的size變了(320,568) 這時autoresizingMask派上了用場(為啥這時候不用Autolayout? 因為還要支持ios5唄) 簡單的適配一下即可。

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



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

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






系統(tǒng)約束

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

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

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

//imgV左側(cè)與父視圖左側(cè) 多出10個PX
NSLayoutConstraint* leftConstraint = [NSLayoutConstraint constraintWithItem:imgV attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeLeading multiplier:1.0f constant:10.0f];
//imgV右側(cè)與父視圖右側(cè)對齊
NSLayoutConstraint* rightConstraint = [NSLayoutConstraint constraintWithItem:imgV attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeTrailing multiplier:1.0f constant:0.0f];
//imgV頂部與父視圖頂部 多出20個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;

屬性過多,代碼冗余繁雜?。?!
反正我是不想用。

iPhone6s 效果圖






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

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

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

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






三方庫Masonry

主角 Masonry登場了

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

Masonry支持的一些屬性:

make是MASConstraintMaker類型。MASConstraintMaker給我們提供了22種Attribute類型

  //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的對照表如下:


系統(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

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

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


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

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

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

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


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

先介紹一個MACRO (因為使用了Block,需要弱引用)

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


首先在Masonry中能夠添加autolayout約束有三個函數(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不能同時存在  兩條針對于  同一對象 的約束 否則會報錯
         mas_updateConstraints 針對上面的情況 會  更新  在block中出現(xiàn)的約束 不會導(dǎo)致出現(xiàn)兩個相同約束的情況
         mas_remakeConstraints 則會  清除 之前的 所有約束 僅保留最新的約束
         三種函數(shù)善加利用 就可以應(yīng)對各種情況了
   */

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

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

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

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

  #import <Masonry.h>

再寫一些后面需要的常用定義宏

 #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個圖片試圖
    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];
  //隨機色生成

    [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);             //中心 對齊
    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)嵌
    
    /* 等價于 */
    //  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);
    
    /* 也等價于 */
   //  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)換為 視圖格式(??  才對應(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類型
    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);
}];


//    動畫
[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];
}];
//   因為布局約束就是要 脫離frame表達(dá)方式,可是動畫是需要根據(jù)這個來執(zhí)行,就會有
 矛盾,但根據(jù)前面說到布局約束的原理,在 某個時刻 約束也是會被 還原 成frame使視圖
顯示,這個時刻可通過 layoutIfNeeded這個方法來進(jìn)行控制。

效果:

最終效果圖.png


  • 因為布局約束就是要 脫離frame表達(dá)方式,可是動畫是需要根據(jù)這個來執(zhí)行,就會有矛盾,但根據(jù)前面說到布局約束的原理,在 某個時刻 約束也是會被 還原 成frame使視圖顯示,這個時刻可通過 layoutIfNeeded這個方法來進(jìn)行控制。

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





Masonry的屬性和類別




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

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

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

之后有時間討論UIScrollView的約束建立

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

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
















goyohol's essay

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

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

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