iOS Masonry布局(一)

Masonry是項(xiàng)目中常見(jiàn)的自動(dòng)布局庫(kù)怀酷,采用鏈?zhǔn)秸Z(yǔ)法封裝蛇券。

常見(jiàn)布局方法

1.添加約束

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

2.更新約束

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

3.刪除以前的約束剃毒,重新約束

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

添加約束前需將試圖添加到父試圖,否則將引起崩潰

等間距布局

適合固定視圖洁闰,內(nèi)部沒(méi)有清空原有視圖的布局歉甚,所以數(shù)組內(nèi)視圖變動(dòng)后再調(diào)用該方法更新布局會(huì)引起UI異常。

/**
 *  distribute with fixed spacing
 *
 *  @param axisType     橫向排列或豎行排列
 *  @param fixedSpacing 視圖間隔大小
 *  @param leadSpacing  第一個(gè)視圖距邊緣間隔大小
 *  @param tailSpacing  最后一個(gè)視圖距邊緣間隔大小
 */
- (void)mas_distributeViewsAlongAxis:(MASAxisType)axisType
                    withFixedSpacing:(CGFloat)fixedSpacing
                         leadSpacing:(CGFloat)leadSpacing
                         tailSpacing:(CGFloat)tailSpacing

示例:

    NSArray *arrays = @[redView,blueView,yellowView,brownView];
    
    [arrays mas_distributeViewsAlongAxis:MASAxisTypeHorizontal
                        withFixedSpacing:10.f
                             leadSpacing:30.f
                             tailSpacing:30.f];
    [arrays mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(self.view.mas_top).offset(150.f);
        make.height.mas_equalTo(50.f);
    }];
等間距.png
等寬布局

適合固定視圖扑眉,內(nèi)部沒(méi)有清空原有視圖的布局纸泄,所以數(shù)組內(nèi)視圖變動(dòng)后再調(diào)用該方法更新布局會(huì)引起UI異常。

/**
 *  distribute with fixed item size
 *
 *  @param axisType        橫向排列或豎行排列
 *  @param fixedItemLength 視圖寬或高
 *  @param leadSpacing     第一個(gè)視圖距邊緣間隔大小
 *  @param tailSpacing     最后一個(gè)視圖距邊緣間隔大小
 */
- (void)mas_distributeViewsAlongAxis:(MASAxisType)axisType withFixedItemLength:(CGFloat)fixedItemLength leadSpacing:(CGFloat)leadSpacing tailSpacing:(CGFloat)tailSpacing;

示例:

    [arrays mas_distributeViewsAlongAxis:MASAxisTypeHorizontal
                     withFixedItemLength:75.f
                             leadSpacing:30.f
                             tailSpacing:30.f];
    [arrays mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(self.view.mas_top).offset(150.f);
        make.height.mas_equalTo(50.f);
    }];
等寬.png
leading腰素、trailing

相較于left聘裁、right能夠自動(dòng)改變文字方向(如阿拉伯語(yǔ))。

間隔布局 - offset

offset常用于設(shè)置兩個(gè)視圖邊緣的間隔弓千,同時(shí)可以在布局完成后動(dòng)態(tài)的改動(dòng)視圖間的間隔大小衡便。

示例:視圖左側(cè)相距頁(yè)面左邊緣20像素。

@property (nonatomic, strong) MASConstraint *redViewLeftConstraint;

#pragma mark - 布局
    [redView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(self.view.mas_top).offset(130.f);
        _redViewLeftConstraint = make.left.equalTo(self.view.mas_left).offset(20.f);
        make.width.mas_equalTo(180.f);
        make.height.mas_equalTo(90.f);
    }];
左布局.png

改變視圖左間距(此時(shí)間距值會(huì)直接覆蓋上次設(shè)置的間隔大小,不會(huì)累加)镣陕。

    _redViewLeftConstraint.offset(100.f);
offset.png

示例:視圖右側(cè)相距頁(yè)面右邊緣20像素谴餐。

@property (nonatomic, strong) MASConstraint *redViewRightConstraint;

#pragma mark - 布局
    [redView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(self.view.mas_top).offset(130.f);
        _redViewRightConstraint = make.right.equalTo(self.view.mas_right).offset(-20.f);
        make.width.mas_equalTo(180.f);
        make.height.mas_equalTo(90.f);
    }];
右布局.png

改變視圖間距

    _redViewRightConstraint.offset(-100.f);
offsetR.png

結(jié)論:第二個(gè)視圖相對(duì)第一個(gè)視圖右移時(shí)offset(正數(shù)),第二個(gè)視圖相對(duì)第一個(gè)視圖左移時(shí)offset(負(fù)數(shù))呆抑。

equalTo與mas_equalTo區(qū)別

equalTo(value)等于value值岂嗓。

    [redView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(self.view.mas_top).offset(130.f);
        make.left.equalTo(self.view.mas_left).offset(20.f);
        make.width.mas_equalTo(180.f);
        make.height.mas_equalTo(90.f);
    }];
    redView.text = @"redViewredView";
redViewredView.png
    redView.text = @"redViewredViewredViewredView";
redViewredViewredViewredView.png

如圖所示:視圖寬度固定180像素,不會(huì)因內(nèi)容的改變而改變理肺。

  • 區(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__)))

mas_equalTo只是對(duì)其參數(shù)進(jìn)行了一個(gè)BOX(裝箱) 操作摄闸,目前支持的類(lèi)型:數(shù)值類(lèi)型(NSNumber)、 點(diǎn)(CGPoint)妹萨、大心暾怼(CGSize)、邊距(UIEdgeInsets)乎完,而equalTo這個(gè)方法不會(huì)對(duì)參數(shù)進(jìn)行包裝熏兄。

lessThanOrEqualTo

lessThanOrEqualTo(value)少于或等于value值,常用于內(nèi)容可能動(dòng)態(tài)改變情況树姨。

示例:寬度少于等于180像素摩桶。

    [redView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(self.view.mas_top).offset(130.f);
        make.left.equalTo(self.view.mas_left).offset(20.f);
        make.width.mas_lessThanOrEqualTo(180.f);
        make.height.mas_equalTo(90.f);
    }];
    redView.text = @"redView";
redView.png
    redView.text = @"redViewredViewredView";
redViewredViewredView.png

如圖所示,視圖寬度會(huì)隨著內(nèi)容動(dòng)態(tài)改變帽揪,但是最大不超過(guò)180像素硝清,超出內(nèi)容省略展示。

示例:此處紅色視圖右邊緣距父視圖右邊緣間隔大于20像素转晰,但是上節(jié)中介紹第二個(gè)視圖相對(duì)第一個(gè)視圖左移時(shí)offset(負(fù)數(shù))芦拿,故此處使用lessThanOrEqualTo。

    [redView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(self.view.mas_top).offset(130.f);
        make.left.equalTo(self.view.mas_left).offset(20.f);
        make.right.lessThanOrEqualTo(self.view.mas_right).offset(-20.f);
        make.width.mas_equalTo(180.f);
        make.height.mas_equalTo(90.f);
    }];
    redView.text = @"redViewredView";
lessThanOrEqualTo.png
greaterThanOrEqualTo

greaterThanOrEqualTo(value)大于或等于value值查邢。

示例:寬度大于等于180像素蔗崎。

    [redView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(self.view.mas_top).offset(130.f);
        make.left.equalTo(self.view.mas_left).offset(20.f);
        make.width.mas_greaterThanOrEqualTo(180.f);
        make.height.mas_equalTo(90.f);
    }];
    redView.text = @"redView";
greaterThanOrEqualTo.png
    redView.text = @"redViewredViewredViewredViewredView";
redViewredViewredViewredViewredView.png

如圖所示:寬度隨著文字的內(nèi)容改變,但即使文字較少時(shí)寬度也不會(huì)小于180像素扰藕。

比例布局 - multipliedBy缓苛、dividedBy

multipliedBy:屬性表示約束值為約束對(duì)象的乘因數(shù)。
dividedBy:屬性表示約束值為約束對(duì)象的除因數(shù)邓深。

示例:紅色視圖寬180高90未桥,藍(lán)色視圖寬180高為寬度的0.5倍數(shù)。

    [redView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(self.view.mas_top).offset(130.f);
        make.left.equalTo(self.view.mas_left).offset(20.f);
        make.width.mas_equalTo(180.f);
        make.height.mas_equalTo(90.f);
    }];
    [blueView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(self.view.mas_top).offset(130.f);
        make.right.equalTo(self.view.mas_right).offset(-20.f);;
        make.width.mas_equalTo(180.f);
        make.height.equalTo(blueView.mas_width).multipliedBy(0.5);
    }];
multipliedBy.png

通過(guò)圖片可知兩個(gè)視圖高度相同芥备,而藍(lán)色視圖0.5倍寬度的高度值正是90冬耿。

快速定位約束沖突視圖 - mas_key

Masonry可以通過(guò)給視圖控件設(shè)置key值來(lái)區(qū)分不同的控件,方便出現(xiàn)約束沖突時(shí)快速定位到有約束沖突的視圖门躯。
示例:

    [redView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(self.view.mas_top).offset(130.f);
        make.left.equalTo(self.view.mas_left).offset(20.f);
        make.right.equalTo(self.view.mas_right).offset(-20.f);
        make.width.mas_equalTo(180.f);
        make.height.mas_equalTo(90.f);
    }];

控制臺(tái)會(huì)提示約束沖突日志如下:

[LayoutConstraints] Unable to simultaneously satisfy constraints.
    Probably at least one of the constraints in the following list is one you don't want. 
    Try this: 
        (1) look at each constraint and try to figure out which you don't expect; 
        (2) find the code that added the unwanted constraint or constraints and fix it. 
(
    "<MASLayoutConstraint:0x6000036ea580 UIView:0x7fcbfe4022f0.left == UIView:0x7fcbfe607e10.left + 20>",
    "<MASLayoutConstraint:0x6000036ea5e0 UIView:0x7fcbfe4022f0.right == UIView:0x7fcbfe607e10.right - 20>",
    "<MASLayoutConstraint:0x6000036e9ec0 UIView:0x7fcbfe4022f0.width == 180>",
    "<NSLayoutConstraint:0x6000031caad0 UIView:0x7fcbfe607e10.width == 414>"
)

Will attempt to recover by breaking constraint 
<MASLayoutConstraint:0x6000036e9ec0 UIView:0x7fcbfe4022f0.width == 180>

當(dāng)應(yīng)用中布局比較多或者布局比較相似時(shí)想快速準(zhǔn)確定位到有沖突的視圖比較困難淆党。

    redView.mas_key = @"redViewKey";
    [redView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(self.view.mas_top).offset(130.f);
        make.left.equalTo(self.view.mas_left).offset(20.f);
        make.right.equalTo(self.view.mas_right).offset(-20.f);
        make.width.mas_equalTo(180.f);
        make.height.mas_equalTo(90.f);
    }];
[LayoutConstraints] Unable to simultaneously satisfy constraints.
    Probably at least one of the constraints in the following list is one you don't want. 
    Try this: 
        (1) look at each constraint and try to figure out which you don't expect; 
        (2) find the code that added the unwanted constraint or constraints and fix it. 
(
    "<MASLayoutConstraint:0x6000001ec6c0 UIView:redViewKey.left == UIView:0x7fe3b6602b00.left + 20>",
    "<MASLayoutConstraint:0x6000001ec1e0 UIView:redViewKey.right == UIView:0x7fe3b6602b00.right - 20>",
    "<MASLayoutConstraint:0x6000001ec720 UIView:redViewKey.width == 180>",
    "<NSLayoutConstraint:0x6000006d0910 UIView:0x7fe3b6602b00.width == 414>"
)

Will attempt to recover by breaking constraint 
<MASLayoutConstraint:0x6000001ec720 UIView:redViewKey.width == 180>

對(duì)比設(shè)置mas_key前后的沖突日志可以發(fā)現(xiàn)酷师,設(shè)置后日志會(huì)直接顯示沖突視圖的key值(UIView:redViewKey.right == ...)讶凉,這樣我們就能快速準(zhǔn)確的定位到約束沖突視圖染乌。

優(yōu)先級(jí) - priority

每個(gè)約束都有一個(gè)優(yōu)先級(jí),優(yōu)先級(jí)的范圍是1 ~ 1000懂讯,默認(rèn)創(chuàng)建的約束優(yōu)先級(jí)是MASLayoutPriorityRequired(1000)荷憋。
作用:優(yōu)先實(shí)現(xiàn)優(yōu)先級(jí)高的設(shè)置,發(fā)生沖突時(shí)褐望,放棄優(yōu)先級(jí)低的設(shè)置勒庄。

常見(jiàn)優(yōu)先級(jí)設(shè)置:

  • .priority() 設(shè)定一個(gè)明確的優(yōu)先級(jí)的值。
  • .priorityHigh() 無(wú)參數(shù)瘫里,使用預(yù)設(shè)的MASLayoutPriorityDefaultHigh(750)值实蔽。
  • .priorityMedium(),使用默認(rèn)的MASLayoutPriorityDefaultMedium(500)值谨读。
  • .priorityLow()局装,使用默認(rèn)的MASLayoutPriorityDefaultLow(250)值。

如上節(jié)中約束沖突可以通過(guò)設(shè)置優(yōu)先級(jí)解決:

    [redView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(self.view.mas_top).offset(130.f);
        make.left.equalTo(self.view.mas_left).offset(20.f).priorityHigh();
        make.right.equalTo(self.view.mas_right).offset(-20.f).priority(740);
        make.width.mas_equalTo(180.f);
        make.height.mas_equalTo(90.f);
    }];
優(yōu)先級(jí).png

priorityHigh()優(yōu)先級(jí)高于priority(740)劳殖,故make.left.equalTo(self.view.mas_left).offset(20.f).priorityHigh()生效铐尚,結(jié)果如圖所示。

單個(gè)約束的生效與刪除 - uninstall哆姻、install

install:約束生效宣增。
uninstall:約束刪除。
示例:

@property (nonatomic, strong) MASConstraint *redViewLeftConstraint;
@property (nonatomic, strong) MASConstraint *redViewRightConstraint;

#pragma mark - 布局
[redView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(self.view.mas_top).offset(130.f);
        _redViewLeftConstraint = make.left.equalTo(self.view.mas_left).offset(20.f).priorityHigh();
        _redViewRightConstraint = make.right.equalTo(self.view.mas_right).offset(-20.f).priority(740);
        make.width.mas_equalTo(180.f);
        make.height.mas_equalTo(90.f);
    }];

#pragma mark - 更改約束
[_redViewLeftConstraint uninstall];
[_redViewRightConstraint install];

install.png

如圖所示:使用install矛缨、uninstall更改約束后爹脾,視圖變?yōu)榫嘤疫吘?0像素。

提高可讀性 - with劳景、and
- (MASConstraint *)with {
    return self;
}

- (MASConstraint *)and {
    return self;
}

查看方法代碼可知兩個(gè)方法沒(méi)有做任何操作誉简,只是返回對(duì)象本身,兩個(gè)方法的作用是為了提高可讀性盟广。

動(dòng)畫(huà)
    _redViewRightConstraint.offset(-100.f);
    
    //告知需要更新約束闷串,但不會(huì)立刻開(kāi)始,系統(tǒng)然后調(diào)用updateConstraints
    [self.view setNeedsUpdateConstraints];
    //告知立刻更新約束筋量,系統(tǒng)立即調(diào)用updateConstraints
    [self.view  updateConstraintsIfNeeded];
    [UIView animateWithDuration:0.4 animations:^{
        //告知頁(yè)面立刻刷新烹吵,系統(tǒng)立即調(diào)用updateConstraints
        [self.view  layoutIfNeeded];
    }];
updateConstraints
  • 重置/更新約束在這個(gè)方法(updateConstraints)內(nèi)
- (void)updateConstraints {
    [self.redView mas_remakeConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(self.view.mas_top).offset(130.f);
        make.left.equalTo(self.view.mas_left).offset(20.f);
        make.width.mas_equalTo(180.f);
        make.height.mas_equalTo(90.f);
    }];
    [super updateConstraints];
}
  • 設(shè)置requiresConstraintBasedLayout為YES
+ (BOOL)requiresConstraintBasedLayout {
    return YES;
}
注意

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市呀酸,隨后出現(xiàn)的幾起案子凉蜂,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,682評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件窿吩,死亡現(xiàn)場(chǎng)離奇詭異茎杂,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)纫雁,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)煌往,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人轧邪,你說(shuō)我怎么就攤上這事刽脖。” “怎么了忌愚?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,083評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵曲管,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我硕糊,道長(zhǎng)翘地,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,763評(píng)論 1 295
  • 正文 為了忘掉前任癌幕,我火速辦了婚禮衙耕,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘勺远。我一直安慰自己橙喘,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,785評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布胶逢。 她就那樣靜靜地躺著厅瞎,像睡著了一般。 火紅的嫁衣襯著肌膚如雪初坠。 梳的紋絲不亂的頭發(fā)上和簸,一...
    開(kāi)封第一講書(shū)人閱讀 51,624評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音碟刺,去河邊找鬼锁保。 笑死,一個(gè)胖子當(dāng)著我的面吹牛半沽,可吹牛的內(nèi)容都是我干的爽柒。 我是一名探鬼主播,決...
    沈念sama閱讀 40,358評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼者填,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼浩村!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起占哟,我...
    開(kāi)封第一講書(shū)人閱讀 39,261評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤心墅,失蹤者是張志新(化名)和其女友劉穎酿矢,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體怎燥,經(jīng)...
    沈念sama閱讀 45,722評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡棠涮,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了刺覆。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,030評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡史煎,死狀恐怖谦屑,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情篇梭,我是刑警寧澤氢橙,帶...
    沈念sama閱讀 35,737評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站恬偷,受9級(jí)特大地震影響悍手,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜袍患,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,360評(píng)論 3 330
  • 文/蒙蒙 一坦康、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧诡延,春花似錦滞欠、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,941評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至惹恃,卻和暖如春夭谤,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背巫糙。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,057評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工朗儒, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人参淹。 一個(gè)月前我還...
    沈念sama閱讀 48,237評(píng)論 3 371
  • 正文 我出身青樓采蚀,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親承二。 傳聞我的和親對(duì)象是個(gè)殘疾皇子榆鼠,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,976評(píng)論 2 355

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