使用Masonry(AutoLayout)出現(xiàn)約束沖突的解決方案

大家都知道既们,系統(tǒng)要展示一個(gè) UIView 居扒,必須要知道它的位置和大小。
在不使用 AutoLayout 的時(shí)候暂题,我們通過設(shè)置 frame 屬性來告訴系統(tǒng)這個(gè) UIView 的位置和大小勋磕。
如果使用了 AutoLayout ,則需要我們?yōu)?UIView 添加一些約束來讓系統(tǒng)自己計(jì)算 UIView 的位置和大小敢靡。

為什么會(huì)產(chǎn)生約束沖突

想要解決約束沖突挂滓,首先我們要先知道為什么會(huì)產(chǎn)生約束沖突。

場景一

假設(shè)我有兩個(gè) UIView 啸胧,分別為 view1view2 赶站, view1 的位置和大小確定, view2 的頂部距離 view1 的底部距離固定纺念, view2 的左邊距贝椿、大小和 view1 相等。如以下代碼:

[view1 mas_makeConstraints:^(MASConstraintMaker *make) {
    make.top.mas_equalTo(50);
    make.left.mas_equalTo(20);
    make.width.mas_equalTo(200);
    make.height.mas_equalTo(50);
}];
    
[view2 mas_makeConstraints:^(MASConstraintMaker *make) {
    make.top.equalTo(view1.mas_bottom).offset(10);
    make.left.height.width.equalTo(view1);
}];

這時(shí)陷谱,運(yùn)行后的結(jié)果如下圖:

1.png

這是一個(gè)最簡單的使用 Masonry 布局例子烙博。

場景二

如果我要給 view2 增加一個(gè)約束:寬度不能大于100瑟蜈,也就是如果 view1 的寬度小于100時(shí),我要讓 view2 的寬度等于 view1 的寬度渣窜,而如果 view1 的寬度大于100時(shí)铺根, view2 的寬度等于100。如果直接這樣設(shè)置 view2 的約束:

[view2 mas_makeConstraints:^(MASConstraintMaker *make) {
    make.top.equalTo(view1.mas_bottom).offset(10);
    make.left.height.width.equalTo(view1);
    make.width.mas_lessThanOrEqualTo(100);
}];

那么系統(tǒng)會(huì)報(bào)警告乔宿,提示約束沖突:

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:0x6000000b5720 UIView:0x7fb8fb008870.width == 200>",
    "<MASLayoutConstraint:0x6000000b5d20 UIView:0x7fb8fb00c150.width == UIView:0x7fb8fb008870.width>",
    "<MASLayoutConstraint:0x6000000b5de0 UIView:0x7fb8fb00c150.width <= 100>"
)

Will attempt to recover by breaking constraint 
<MASLayoutConstraint:0x6000000b5de0 UIView:0x7fb8fb00c150.width <= 100>

Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.

那么為什么會(huì)產(chǎn)生這樣的沖突呢位迂?原因很簡單,view1 的寬度等于200详瑞,而 view2 的寬度等于 view1 的寬度掂林,所以它的寬度也應(yīng)為200,但 view2 的寬度不能大于100坝橡,所以系統(tǒng)不知道 view2 的寬度到底是多少泻帮。

解決約束沖突

通過上面這個(gè)例子,就引出了我們解決約束沖突的方法:設(shè)置優(yōu)先級(jí)计寇。

當(dāng)兩個(gè)約束產(chǎn)生沖突時(shí)锣杂,iOS會(huì)棄用優(yōu)先級(jí)低的約束,而選擇優(yōu)先級(jí)較高的約束饲常,達(dá)到正確布局的效果蹲堂。

所以,針對(duì)上面那種情況贝淤,我們只需將 view2 的約束寫為以下這種就行了:

[view2 mas_makeConstraints:^(MASConstraintMaker *make) {
    make.top.equalTo(view1.mas_bottom).offset(10);
    make.left.height.equalTo(view1);
    make.width.equalTo(view1).priorityLow();
    make.width.mas_lessThanOrEqualTo(100);
}];

效果如下圖:

2.png

延伸--IntrinsicContentSize

大家可能有時(shí)會(huì)發(fā)現(xiàn)柒竞,我們?cè)谠O(shè)置 UILabel 的約束時(shí),可以不設(shè)置它的大小約束播聪,這樣它就會(huì)根據(jù)自己的內(nèi)容(文字)來確定它的大小朽基。如一下場景:

場景三

兩個(gè) UILabel,分別為 label1label2 离陶,其中 label1 左邊距和上邊距確定稼虎, label2 的上邊距和 label1 相同,左邊距離 label1 的右邊距離確定招刨,如以下約束:

[label1 mas_makeConstraints:^(MASConstraintMaker *make) {
    make.top.mas_equalTo(200);
    make.left.mas_equalTo(20);
}];
    
[label2 mas_makeConstraints:^(MASConstraintMaker *make) {
    make.left.equalTo(label1.mas_right).offset(10);
    make.top.equalTo(label1);
}];

我們并沒有設(shè)置兩個(gè) UILabel 的大小約束霎俩,運(yùn)行效果如下:

3.png

可以發(fā)現(xiàn),兩個(gè) UILabel 的寬度和高度都根據(jù)它們的內(nèi)容來設(shè)置了沉眶。而這是為什么呢打却?原來 UIView 具有一個(gè)屬性: CGSize intrinsicContentSize ,含義如下:

Intrinsic Content Size:固有大小谎倔。
顧名思義柳击,在AutoLayout中,如果沒有指定大小片习,那么 UIView 的大小就按照 intrinsicContentSize 來設(shè)置捌肴。

所以蹬叭,雖然我們沒有設(shè)置這兩個(gè) UILabel 的大小約束,它們還是根據(jù)自己的內(nèi)容設(shè)置好了大小状知,展示出來秽五。
CGSize intrinsicContentSize 這個(gè)屬性是 readOnly 的,所以如果我們要修改它试幽,需要定義一個(gè)子類筝蚕,重寫這個(gè)屬性的 getter 方法卦碾。
值得一提的是如果我們不想使用這個(gè)屬性铺坞,則可以在重寫的 getter 方法中直接返回 return CGSizeMake(UIViewNoIntrinsicMetric, UIViewNoIntrinsicMetric); 就行了。

場景四

現(xiàn)在洲胖,我要增加一個(gè) label2 的約束:讓它的右邊距距離父視圖的最右邊20px济榨,如下:

[label2 mas_makeConstraints:^(MASConstraintMaker *make) {
    make.left.equalTo(label1.mas_right).offset(10);
    make.top.equalTo(label1);
    make.right.equalTo(self.view.mas_right).offset(-20);
}];

此時(shí),就產(chǎn)生了一個(gè)問題:正常情況下绿映, label2 的最右邊是不可能只距離父視圖20px的擒滑,這就使得肯定有一個(gè) UILabel 不能使用它的 intrinsicContentSize 了,那么應(yīng)該修改哪個(gè) UILabel 的寬度來讓 label2 滿足這個(gè)約束呢叉弦?
解決方案還是設(shè)置優(yōu)先級(jí)丐一,但這次我們需要設(shè)置的是兩種約束的優(yōu)先級(jí): contentHuggingcontentCompressionResistance

contentHugging(不想變大約束):如果組件的此屬性優(yōu)先級(jí)比另一個(gè)組件此屬性優(yōu)先級(jí)高的話淹冰,那么這個(gè)組件就保持不變库车,另一個(gè)可以在需要拉伸的時(shí)候拉伸。
contentCompressionResistance (不想變小約束):如果組件的此屬性優(yōu)先級(jí)比另一個(gè)組件此屬性優(yōu)先級(jí)高的話樱拴,那么這個(gè)組件就保持不變柠衍,另一個(gè)可以在需要壓縮的時(shí)候壓縮。

所以晶乔,如果我們需要 label1 不使用 intrinsicContentSize 珍坊, label2 使用 intrinsicContentSize ,則可以將 label1contentHugging 優(yōu)先級(jí)設(shè)為低優(yōu)先級(jí)正罢,反之阵漏,如果我們需要 label1 使用 intrinsicContentSize ,則可以將其 contentHugging 的優(yōu)先級(jí)設(shè)為高優(yōu)先級(jí)翻具。
那么怎么設(shè)置它們這兩個(gè)約束的優(yōu)先級(jí)呢履怯?
UIView 提供了兩個(gè)方法給我們?cè)O(shè)置它的這兩個(gè)約束的優(yōu)先級(jí):

- (void)setContentHuggingPriority:(UILayoutPriority)priority forAxis:(UILayoutConstraintAxis)axis;
- (void)setContentCompressionResistancePriority:(UILayoutPriority)priority forAxis:(UILayoutConstraintAxis)axis;

其中,第一個(gè)參數(shù)為優(yōu)先級(jí)呛占,具體有以下幾種:

static const UILayoutPriority UILayoutPriorityRequired NS_AVAILABLE_IOS(6_0) = 1000; // A required constraint.  Do not exceed this.
static const UILayoutPriority UILayoutPriorityDefaultHigh NS_AVAILABLE_IOS(6_0) = 750; // This is the priority level with which a button resists compressing its content.
static const UILayoutPriority UILayoutPriorityDefaultLow NS_AVAILABLE_IOS(6_0) = 250; // This is the priority level at which a button hugs its contents horizontally.
static const UILayoutPriority UILayoutPriorityFittingSizeLevel NS_AVAILABLE_IOS(6_0) = 50;

當(dāng)然虑乖,你也可以自己通過數(shù)字來設(shè)置它們的優(yōu)先級(jí)。
第二個(gè)參數(shù)代表你要設(shè)置約束優(yōu)先級(jí)的方向晾虑,分為橫向和縱向:

typedef NS_ENUM(NSInteger, UILayoutConstraintAxis) {
    UILayoutConstraintAxisHorizontal = 0,
    UILayoutConstraintAxisVertical = 1
};

以上場景疹味,我們可以通過以下代碼來讓 label1 使用 intrinsicContentSize 仅叫, label2 不使用 intrinsicContentSize

[label1 setContentHuggingPriority:UILayoutPriorityDefaultHigh forAxis:UILayoutConstraintAxisHorizontal];

效果如下圖:

4.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市糙捺,隨后出現(xiàn)的幾起案子诫咱,更是在濱河造成了極大的恐慌,老刑警劉巖洪灯,帶你破解...
    沈念sama閱讀 216,324評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件坎缭,死亡現(xiàn)場離奇詭異,居然都是意外死亡签钩,警方通過查閱死者的電腦和手機(jī)掏呼,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,356評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來铅檩,“玉大人憎夷,你說我怎么就攤上這事∶林迹” “怎么了拾给?”我有些...
    開封第一講書人閱讀 162,328評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長兔沃。 經(jīng)常有香客問我蒋得,道長,這世上最難降的妖魔是什么乒疏? 我笑而不...
    開封第一講書人閱讀 58,147評(píng)論 1 292
  • 正文 為了忘掉前任额衙,我火速辦了婚禮,結(jié)果婚禮上缰雇,老公的妹妹穿的比我還像新娘入偷。我一直安慰自己,他們只是感情好械哟,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,160評(píng)論 6 388
  • 文/花漫 我一把揭開白布疏之。 她就那樣靜靜地躺著,像睡著了一般暇咆。 火紅的嫁衣襯著肌膚如雪锋爪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,115評(píng)論 1 296
  • 那天爸业,我揣著相機(jī)與錄音其骄,去河邊找鬼。 笑死扯旷,一個(gè)胖子當(dāng)著我的面吹牛拯爽,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播钧忽,決...
    沈念sama閱讀 40,025評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼毯炮,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼逼肯!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起桃煎,我...
    開封第一講書人閱讀 38,867評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤篮幢,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后为迈,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體三椿,經(jīng)...
    沈念sama閱讀 45,307評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,528評(píng)論 2 332
  • 正文 我和宋清朗相戀三年葫辐,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了搜锰。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,688評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡另患,死狀恐怖纽乱,靈堂內(nèi)的尸體忽然破棺而出蛾绎,到底是詐尸還是另有隱情昆箕,我是刑警寧澤,帶...
    沈念sama閱讀 35,409評(píng)論 5 343
  • 正文 年R本政府宣布租冠,位于F島的核電站鹏倘,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏顽爹。R本人自食惡果不足惜纤泵,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,001評(píng)論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望镜粤。 院中可真熱鬧捏题,春花似錦、人聲如沸肉渴。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,657評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽同规。三九已至循狰,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間券勺,已是汗流浹背绪钥。 一陣腳步聲響...
    開封第一講書人閱讀 32,811評(píng)論 1 268
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留关炼,地道東北人程腹。 一個(gè)月前我還...
    沈念sama閱讀 47,685評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像儒拂,于是被迫代替她去往敵國和親寸潦。 傳聞我的和親對(duì)象是個(gè)殘疾皇子缀去,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,573評(píng)論 2 353

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