代碼適配Masonry使用的詳細(xì)介紹

Masonry簡(jiǎn)介

Masonry是一個(gè)輕量級(jí)的布局框架瓦盛,它擁有自己的描述語(yǔ)法(采用更優(yōu)雅的鏈?zhǔn)秸Z(yǔ)法封裝)來(lái)自動(dòng)布局贷腕,具有很好可讀性且同時(shí)支持iOS和Max OS X等。
總之蔓涧,對(duì)于側(cè)重寫代碼的coder构灸,請(qǐng)你慢慢忘記Frame上渴,喜歡Masonry

使用前的準(zhǔn)備

若是你對(duì)于自動(dòng)布局很熟練的話,再接觸這個(gè)第三方Masonry很容易上手的喜颁,對(duì)UI界面顯示的控件的約束本質(zhì)都是相同的稠氮,現(xiàn)在呢,我一般都是喜歡在控制器里導(dǎo)入 #import "Masonry.h"之前再添加兩個(gè)宏半开,來(lái)提高App的開發(fā)效率括袒。

//1. 對(duì)于約束參數(shù)可以省去"mas_"
#define MAS_SHORTHAND
//2. 對(duì)于默認(rèn)的約束參數(shù)自動(dòng)裝箱
#define MAS_SHORTHAND_GLOBALS

即:需要我們導(dǎo)入的框架與宏如下

//define this constant if you want to use Masonry without the 'mas_' prefix
#define MAS_SHORTHAND
//define this constant if you want to enable auto-boxing for default syntax
#define MAS_SHORTHAND_GLOBALS

#import "Masonry.h" //宏必須添加在頭文件前面

添加約束前提:被約束的必須有父控件,其中約束項(xiàng)都必須是UIView或子類的實(shí)例

約束的屬性

在此我就列舉幾個(gè)可能不太熟悉的吧

@property (nonatomic, strong, readonly) MASConstraint *leading;  //首部
@property (nonatomic, strong, readonly) MASConstraint *trailing; //尾部
@property (nonatomic, strong, readonly) MASConstraint *baseline; //文本基線

約束的三種方法

/**
 //這個(gè)方法只會(huì)添加新的約束
 [blueView mas_makeConstraints:^(MASConstraintMaker *make)  {
 
 }];
 
 // 這個(gè)方法會(huì)將以前的所有約束刪掉稿茉,添加新的約束
 [blueView mas_remakeConstraints:^(MASConstraintMaker *make) {
 
 }];
 
 // 這個(gè)方法將會(huì)覆蓋以前的某些特定的約束
 [blueView mas_updateConstraints:^(MASConstraintMaker *make) {
 
 }];
 */

常見約束的各種類型

/**
 1.尺寸:width、height芥炭、size
 2.邊界:left漓库、leading、right园蝠、trailing渺蒿、top、bottom
 3.中心點(diǎn):center彪薛、centerX茂装、centerY
 4.邊界:edges
 5.偏移量:offset、insets善延、sizeOffset少态、centerOffset
 6.priority()約束優(yōu)先級(jí)(0~1000),multipler乘因數(shù), dividedBy除因數(shù)
 */

Masonry約束易忽略的技術(shù)點(diǎn)

使用Masonry不需要設(shè)置控件的translatesAutoresizingMaskIntoConstraints屬性為NO;
防止block中的循環(huán)引用易遣,使用弱引用(這是錯(cuò)誤觀點(diǎn))彼妻,在這里block是局部的引用,block內(nèi)部引用self不會(huì)造成循環(huán)引用的
__weak typeof (self) weakSelf = self;(沒必要的寫法)

Masonry約束控件出現(xiàn)沖突的問(wèn)題

當(dāng)約束沖突發(fā)生的時(shí)候豆茫,我們可以設(shè)置view的key來(lái)定位是哪個(gè)view

1. 挨個(gè)設(shè)置對(duì)應(yīng)的key
redView.mas_key = @"redView";
greenView.mas_key = @"greenView";

2. 使用Masonry提供的宏 MASAttachKeys
MASAttachKeys(redView); 
MASAttachKeys(redView,greenView);  // 可批量設(shè)置

約束iconView距離各個(gè)邊距為30

    [iconView makeConstraints:^(MASConstraintMaker *make) {
      make.edges.equalTo(self.view).insets(UIEdgeInsetsMake(30, 30, 30, 30));
    }];

equalTo 和 mas_equalTo的區(qū)別

#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__)))

得出結(jié)論:mas_equalTo只是對(duì)其參數(shù)進(jìn)行了一個(gè)BOX(裝箱) 操作侨歉,目前支持的類型:數(shù)值類型(NSNumber)、 點(diǎn)(CGPoint)揩魂、大杏牡恕(CGSize)、邊距(UIEdgeInsets)火脉,而equalTo:這個(gè)方法不會(huì)對(duì)參數(shù)進(jìn)行包裝牵舵。

//常見約束的寫法 
[iconView makeConstraints:^(MASConstraintMaker *make) {
        make.right.equalTo(self.view).offset(-30);
        make.top.equalTo(self.view).offset(30);
        make.height.width.equalTo(100); //== make.size.equalTo(100);

        //make.size.mas_equalTo(self.view).multipliedBy(0.5);
        //make.top.greaterThanOrEqualTo(self.view).offset(padding);
    }];
[redView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(superview.mas_top).with.offset(10);  //with 增強(qiáng)可讀性
        make.left.equalTo(greenView.mas_right).and.offset(10); //and 增強(qiáng)可讀性
        make.bottom.equalTo(blueView.mas_top).offset(-10);
        make.right.equalTo(superview.mas_right).offset(-10);
        make.width.equalTo(greenView.mas_width);
        
        make.height.equalTo(@[greenView, blueView]); //約束參數(shù)相同可以通過(guò)數(shù)組
    }];

更新約束的問(wèn)題

例如:控制器有個(gè)按鈕柒啤,若是點(diǎn)擊按鈕,則按鈕本身的大小棋枕、位置會(huì)隨機(jī)改變

  • 監(jiān)聽按鈕點(diǎn)擊
[self.btn addTarget:self action:@selector(didClickBtn:) forControlEvents:UIControlEventTouchUpInside];
  • 處理事件
 (void) didClickBtn:(UIButton *)button {
    self.btnSize = CGSizeMake(self.btnSize.width * 1.3, self.btnSize.height * 1.3); //設(shè)置一個(gè)屬性(btnSize)保存其大小的變化

    //1.告知需要更新約束白修,但不會(huì)立刻開始,系統(tǒng)然后調(diào)用updateConstraints
    [self setNeedsUpdateConstraints];

    //2.告知立刻更新約束重斑,系統(tǒng)立即調(diào)用updateConstraints
    [self updateConstraintsIfNeeded];

    //3.這里動(dòng)畫當(dāng)然可以取消兵睛,具體看項(xiàng)目的需求
    //系統(tǒng)block內(nèi)引用不會(huì)導(dǎo)致循環(huán)引用,block結(jié)束就會(huì)釋放引用對(duì)象
    [UIView animateWithDuration:0.4 animations:^{
        [self layoutIfNeeded]; //告知頁(yè)面立刻刷新窥浪,系統(tǒng)立即調(diào)用updateConstraints
    }];
}
  • 蘋果官方建議:添加/更新約束在這個(gè)方法(updateConstraints)內(nèi)
// this is Apple's recommended place for adding/updating constraints
- (void)updateConstraints {
   //更新約束
    [self.btn updateConstraints:^(MASConstraintMaker *make) {
        make.center.equalTo(self);

        make.width.equalTo(@(self.buttonSize.width)).priorityLow();
        make.height.equalTo(@(self.buttonSize.height)).priorityLow();

        make.width.lessThanOrEqualTo(self);
        make.height.lessThanOrEqualTo(self);
    }];
    
    //according to apple super should be called at end of method
    //最后必須調(diào)用父類的更新約束
    [super updateConstraints];
}
  • 設(shè)置requiresConstraintBasedLayout為YES
+ (BOOL)requiresConstraintBasedLayout{
    return YES ; //重寫這個(gè)方法 若視圖基于自動(dòng)布局的
}

重置約束的問(wèn)題

對(duì)于控件的重新約束祖很,則之前的約束都是無(wú)效的,步驟都更新約束一樣的漾脂,只是在updateConstraints方法內(nèi)的約束方法改為了remakeConstraints

//首先監(jiān)聽按鈕
[self.btn addTarget:self action:@selector(didClickBtn:) forControlEvents:UIControlEventTouchUpInside];

//處理事件
- (void) didClickBtn :(UIButton *)button{
   (...) //觸發(fā)條件
    [self setNeedsUpdateConstraints]; 

    [self updateConstraintsIfNeeded];
    
   /**
     *   動(dòng)畫展示變化 - 這句代碼可有可無(wú)假颇,參考項(xiàng)目具體的需求
     *   [UIView animateWithDuration:0.4 animations:^{
     *         [self layoutIfNeeded];
     *   }];
     */
}

//重置約束
- (void)updateConstraints {
    [self.btn remakeConstraints:^(MASConstraintMaker *make) {
      .....
    }];
    [super updateConstraints]; 
}

+ (BOOL)requiresConstraintBasedLayout{
    return YES ; //重寫這個(gè)方法 若視圖基于自動(dòng)布局的
}

多個(gè)(2個(gè)以上)控件的等間隔排序顯示

首先介紹2個(gè)函數(shù)
/**
 *  等間隔排列 - 多個(gè)控件間隔固定,控件長(zhǎng)度/寬度變化
 *  axisType         軸線方向 橫排還是豎排
 *  fixedSpacing     間隔大小 兩個(gè)控件間隔
 *  leadSpacing      頭部間隔 第一個(gè)控件與邊緣的間隔
 *  tailSpacing      尾部間隔 最后一個(gè)控件與邊緣的間隔
 *
 */
- (void)mas_distributeViewsAlongAxis:(MASAxisType)axisType
withFixedSpacing:(CGFloat)fixedSpacing leadSpacing:(CGFloat)leadSpacing
tailSpacing:(CGFloat)tailSpacing;

/**
 *  等間隔排列 - 多個(gè)固定大小固定骨稿,間隔空隙變化
 *  axisType         軸線方向 橫排還是豎排
 *  withFixedItemLength     控件的寬或高
 *  leadSpacing      頭部間隔 第一個(gè)控件與邊緣的間隔
 *  tailSpacing      尾部間隔 最后一個(gè)控件與邊緣的間隔
 *
 */
- (void)mas_distributeViewsAlongAxis:(MASAxisType)axisType
withFixedItemLength:(CGFloat)fixedItemLength
leadSpacing:(CGFloat)leadSpacing
tailSpacing:(CGFloat)tailSpacing;
  1. 設(shè)置水平方向MASAxisTypeHorizontal 若是指定控件的寬withFixedItemLength, 則對(duì)應(yīng)控件只需要設(shè)置其Y軸位置; 若是指定控件的間隔fixedSpacing, 則對(duì)應(yīng)控件需要設(shè)置其Y軸和對(duì)應(yīng)的高度H
  2. 設(shè)置垂直方向MASAxisTypeVertical 若是指定控件的高withFixedItemLength, 則對(duì)應(yīng)控件只需要設(shè)置其X軸位置; 若是指定控件的間隔fixedSpacing, 則對(duì)應(yīng)控件需要設(shè)置其X軸和對(duì)應(yīng)的寬度W
//首先添加5個(gè)視圖
 NSMutableArray *array = [NSMutableArray new];
    for (int i = 0; i < 5; i ++) {
        UIView *view = [UIView new];
        view.backgroundColor = [UIColor greenColor];
        [self addSubview:view];
        [array addObject:view]; //保存添加的控件
    }

//水平方向控件間隔固定等間隔
[array mas_distributeViewsAlongAxis:MASAxisTypeHorizontal withFixedSpacing:15 leadSpacing:10 tailSpacing:10];
            [array makeConstraints:^(MASConstraintMaker *make) {
                make.top.equalTo(50);
                make.height.equalTo(70);
                // make.bottom.equalTo(self);
            }];

//水平方向?qū)挾裙潭ǖ乳g隔
[array mas_distributeViewsAlongAxis:MASAxisTypeHorizontal withFixedItemLength:70 leadSpacing:10 tailSpacing:10];
            [array makeConstraints:^(MASConstraintMaker *make) { //數(shù)組額你不必須都是view 
                make.top.equalTo(50);
            }];
水平方向等間隔.png
水平方向控件寬度固定等間隔.png

多行l(wèi)abel的約束問(wèn)題

對(duì)于UILabel文字內(nèi)容多的問(wèn)題笨鸡,個(gè)人覺得Masonry約束設(shè)置的非常簡(jiǎn)單優(yōu)雅,在此非常感謝Masonry的作者@Robert Payne

    //創(chuàng)建label
    self.label = [UILabel new];
    self.label.numberOfLines = 0;
    self.label.lineBreakMode = NSLineBreakByTruncatingTail;
    self.label.text = @"有的人坦冠,沒事時(shí)喜歡在朋友圈里到處點(diǎn)贊形耗,東評(píng)論一句西評(píng)論一句,比誰(shuí)都有存在感辙浑。等你有事找他了激涤,他就立刻變得很忙,讓你再也找不著判呕。真正的朋友倦踢,平常很少聯(lián)系∠啦荩可一旦你遇上了難處辱挥,他會(huì)立刻回復(fù)你的消息,第一時(shí)間站出來(lái)幫你边涕。所謂的存在感般贼,不是你有沒有出現(xiàn),而是你的出現(xiàn)有沒有價(jià)值奥吩。存在感哼蛆,不是刷出來(lái)的,也不是說(shuō)出來(lái)的霞赫。有存在感腮介,未必是要個(gè)性鋒芒畢露、甚至鋒利扎人端衰。翩翩君子叠洗,溫潤(rùn)如玉甘改,真正有存在感的人,反而不會(huì)刻意去強(qiáng)調(diào)他的存在感灭抑。他的出現(xiàn)十艾,永遠(yuǎn)都恰到好處。我所欣賞的存在感腾节,不是長(zhǎng)袖善舞巧言令色忘嫉,而是對(duì)他人的真心關(guān)照;不是鋒芒畢露計(jì)較勝負(fù)案腺,而是讓人相處得舒服庆冕;不是時(shí)時(shí)刻刻聒噪不休,而是關(guān)鍵時(shí)刻能挺身而出劈榨。別總急著出風(fēng)頭访递,希望你能有恰到好處的存在感。";
    [self addSubview: self.label];

    [self.label makeConstraints:^(MASConstraintMaker *make) {
        make.left.top.equalTo(10);
        make.right.equalTo(-10);
    }];

//添加約束
- (void)layoutSubviews {
    //1. 執(zhí)行 [super layoutSubviews];
    [super layoutSubviews];

    //2. 設(shè)置preferredMaxLayoutWidth: 多行l(wèi)abel約束的完美解決
   self.label.preferredMaxLayoutWidth = [UIScreen mainScreen].bounds.size.width - 20;

    //3. 設(shè)置preferredLayoutWidth后同辣,需要再次執(zhí)行 [super layoutSubviews]; 
    //其實(shí)在實(shí)際中這步不寫拷姿,也不會(huì)出錯(cuò),官方解釋是說(shuō)設(shè)置preferredLayoutWidth后需要重新計(jì)算并布局界面旱函,所以這步最好執(zhí)行
    [super layoutSubviews];
}
多行l(wèi)abel約束.png

UIScrollView的問(wèn)題

原理同自動(dòng)布局一樣 UIScrollView上添加UIView
UIView上添加需要顯示的控件 UIScrollView滾動(dòng)高度取決于顯示控件的總高度
對(duì)子控件做好約束跌前,可達(dá)到控制UIView的大小

    //創(chuàng)建滾動(dòng)視圖
    UIScrollView *scrollView = [UIScrollView new];
    self.scrollView = scrollView;
    
    scrollView.backgroundColor = [UIColor grayColor];
    [self addSubview:scrollView];
    
    [self.scrollView makeConstraints:^(MASConstraintMaker *make) {
        make.edges.equalTo(self);
    }];
    
    [self configUpContentView]; //添加內(nèi)容視圖

- (void)configUpContentView {
    UIView *container = [UIView new];
    [self.scrollView addSubview:container];
    [container makeConstraints:^(MASConstraintMaker *make) {
        make.edges.equalTo(self.scrollView);
        make.width.equalTo(self.scrollView); //此處必填 - 關(guān)鍵點(diǎn)
    }];
    
    //添加控件到contentView,約束原理與自動(dòng)布局相同
    UIView *lastView = nil;
    CGFloat height = 30;
    for (int i = 0; i <1 5; i ++) {
        UIView *view = UIView.new;
        view.backgroundColor = [UIColor colorWithRed:arc4random() % 255 / 256.0  green:arc4random() % 255 / 256.0 blue:arc4random() % 255 / 256.0 alpha:1.0];
        [contentView addSubview:view];

        [view makeConstraints:^(MASConstraintMaker *make) {
            make.top.equalTo(lastView ? lastView.bottom : @0);
            make.left.equalTo(0);
            make.width.equalTo(contentView.width);
            make.height.equalTo(height);
        }];
        
        height += 30;
        lastView = view;
    }
    
    [contentView makeConstraints:^(MASConstraintMaker *make) {
        make.bottom.equalTo(lastView.bottom);
    }];
}
scrollView的約束設(shè)置.png

Masonry源碼GitHub地址下載:Masonry
終于寫完了陡舅,有什么理解不對(duì)的直接說(shuō)...今晚還有巴薩、尤文的歐冠伴挚,真心傷不起靶衍。。茎芋。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末颅眶,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子田弥,更是在濱河造成了極大的恐慌涛酗,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,858評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件偷厦,死亡現(xiàn)場(chǎng)離奇詭異商叹,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)只泼,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門剖笙,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人请唱,你說(shuō)我怎么就攤上這事弥咪」澹” “怎么了?”我有些...
    開封第一講書人閱讀 165,282評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵聚至,是天一觀的道長(zhǎng)酷勺。 經(jīng)常有香客問(wèn)我,道長(zhǎng)扳躬,這世上最難降的妖魔是什么脆诉? 我笑而不...
    開封第一講書人閱讀 58,842評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮坦报,結(jié)果婚禮上库说,老公的妹妹穿的比我還像新娘。我一直安慰自己片择,他們只是感情好潜的,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,857評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著字管,像睡著了一般啰挪。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上嘲叔,一...
    開封第一講書人閱讀 51,679評(píng)論 1 305
  • 那天亡呵,我揣著相機(jī)與錄音,去河邊找鬼硫戈。 笑死锰什,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的丁逝。 我是一名探鬼主播汁胆,決...
    沈念sama閱讀 40,406評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼霜幼!你這毒婦竟也來(lái)了嫩码?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,311評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤罪既,失蹤者是張志新(化名)和其女友劉穎铸题,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體琢感,經(jīng)...
    沈念sama閱讀 45,767評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡丢间,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了驹针。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片千劈。...
    茶點(diǎn)故事閱讀 40,090評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖牌捷,靈堂內(nèi)的尸體忽然破棺而出墙牌,到底是詐尸還是另有隱情涡驮,我是刑警寧澤,帶...
    沈念sama閱讀 35,785評(píng)論 5 346
  • 正文 年R本政府宣布喜滨,位于F島的核電站捉捅,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏虽风。R本人自食惡果不足惜棒口,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,420評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望辜膝。 院中可真熱鬧无牵,春花似錦、人聲如沸厂抖。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)忱辅。三九已至七蜘,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間墙懂,已是汗流浹背橡卤。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評(píng)論 1 271
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留损搬,地道東北人碧库。 一個(gè)月前我還...
    沈念sama閱讀 48,298評(píng)論 3 372
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像巧勤,于是被迫代替她去往敵國(guó)和親嵌灰。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,033評(píng)論 2 355

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