iOS 詳解intrinsicContentSize 及 約束優(yōu)先級/content Hugging/content Compression Resistance

在了解intrinsicContentSize之前,我們需要先了解2個概念:

AutoLayout在做什么

約束優(yōu)先級是什么意思投蝉。

如果不了解這兩個概念,看intinsic content size沒有任何意義硬纤。

注:由于上面這幾個概念都是針對UIView或其子類(UILabel敞峭,UIImageView等等)來說的。所以下文中都用UIView指代球订。

AutoLayout在做什么 – 一個UIView想要顯示在屏幕中后裸,僅須有2個需要確定的元素,一是位置冒滩,二是大小微驶。只要2者確定,UIView就可以正確顯示开睡,至于顯示的內(nèi)容因苹,則由UIView自己決定(drawRect)

沒有AutoLayout的時候,我們需要通過 initWithFrame:(CGRect)這種方式來指定UIView的位置和大小篇恒。

而使用AutoLayout的過程扶檐,就是通過約束來確定UIView的位置和大小的過程。

約束優(yōu)先級 – 為什么約束需要優(yōu)先級胁艰?因為有的時候2個約束可能會有沖突款筑。 比如:有一個UIView 距離父UIView的左右距離都是0,這是2個約束腾么,此時再給此UIView加一個寬度約束奈梳,比如指定寬度為100,那么就會產(chǎn)生約束沖突了

因為解虱,這兩種約束不可能同時存在攘须,只能滿足一個,那么滿足誰呢饭寺?默認(rèn)情況下給UIView加的這幾個約束優(yōu)先級都是1000阻课,屬于最高的優(yōu)先級了,表示此約束必須滿足艰匙。

所以這種沖突不能被iOS所允許限煞。此時就需要修改優(yōu)先級了。把其中任意一個約束的優(yōu)先級改為小于1000的值即可员凝。

iOS可以通過比較兩個”相互沖突的約束”的優(yōu)先級署驻,從而忽略低優(yōu)先級的某個約束,達到正確布局的目的

用鼠標(biāo)選中寬度約束,然后在屏幕右側(cè)的菜單中旺上,修改優(yōu)先級瓶蚂,如下圖

這樣就沒有約束沖突了。因為如果一旦兩個約束沖突宣吱,系統(tǒng)會自動忽略優(yōu)先級低的約束

上面舉的這個例子有些極端窃这,因為上面兩個約束都是確定的值,而且是絕對沖突征候。所以如果遇到這種情況杭攻,可能選擇刪掉某個約束更為合適。

而約束優(yōu)先級更多的時候用于解決模糊約束(相對于上面的確定值約束來說)的沖突的問題疤坝。

比如有這樣一個問題:

UIView1有四個約束:距離父UIView左和上確定兆解,寬和高也確定。

UIView2在UIView1的下面跑揉,約束也有4個:上面距離UIView1確定锅睛,左面同UIView1對齊,同UIView1等高且等寬历谍。

此時這兩個UIView應(yīng)該像這樣

這是一個很普通的應(yīng)用場景现拒,假設(shè)我希望有這樣一個效果: 我希望UIView2的寬度不能超過50。當(dāng)UIView1寬度小于50的時候扮饶,二者等寬具练;當(dāng)UIView1寬度大于50的時候,UIView2不受UIView1寬度的影響甜无。

于是我給UIView2加上一條約束:寬度<=50扛点。這時候沖突來了:

因為UIView1的寬度是定好的,而UIView2和UIView1等寬岂丘。那么UIView2的寬度就是確定的陵究。

很顯然,分為兩種情況(根據(jù)UIView1的寬度不同):

若UIView1的寬度大于50奥帘,UIView2的寬度也一定大于50铜邮,這跟新加的限制寬度<=50的約束是沖突的

否則不沖突。

更糟糕的是寨蹋,實際情況中松蒜,UIView1的寬度可能不是一個確定的值。它有可能會被頁面中的其他View所影響已旧,可能還會在運行時產(chǎn)生變化秸苗,并不能保證它的實際寬度一定小于50。所以运褪,一旦產(chǎn)生約束沖突惊楼,可能就會對應(yīng)用產(chǎn)生不確定的影響:可能顯示錯亂玖瘸,也可能程序崩潰。

所以我們?yōu)榱说玫秸_的結(jié)果檀咙,應(yīng)該這樣處理:

當(dāng)UIView1寬度小于等于50的時候雅倒,約束不沖突,修改優(yōu)先級與否都是一樣結(jié)果弧可。

當(dāng)UIView1寬度大于50的時候蔑匣,忽略等寬約束,也就是降低等寬約束優(yōu)先級侣诺。

所以我們把等寬約束的優(yōu)先級修改為999殖演。上面兩條都滿足,問題解決

說到模糊約束年鸳,content Hugging/content Compression Resistance就是2個UIView自帶的模糊約束。

而這兩個約束存在的條件則是UIView必須指定了 Intrinsic Content Size丸相。

在了解這兩個模糊約束之前搔确,必須了解Intrinsic Content Size是什么東西。

Intrinsic Contenet Size – Intrinsic Content Size:固有大小灭忠。顧名思義膳算,在AutoLayout中,它作為UIView的屬性(不是語法上的屬性)弛作,意思就是說我知道自己的大小涕蜂,如果你沒有為我指定大小,我就按照這個大小來映琳。 比如:大家都知道在使用AutoLayout的時候机隙,UILabel是不用指定尺寸大小的,只需指定位置即可萨西,就是因為有鹿,只要確定了文字內(nèi)容,字體等信息谎脯,它自己就能計算出大小來

UILabel葱跋,UIImageView,UIButton等這些組件及某些包含它們的系統(tǒng)組件都有 Intrinsic Content Size 屬性源梭。

也就是說娱俺,遇到這些組件,你只需要為其指定位置即可废麻。大小就使用Intrinsic Content Size就行了荠卷。

在代碼中,上述系統(tǒng)控件都重寫了UIView 中的 -(CGSize)intrinsicContentSize: 方法脑溢。

并且在需要改變這個值的時候調(diào)用:invalidateIntrinsicContentSize 方法僵朗,通知系統(tǒng)這個值改變了赖欣。

所以當(dāng)我們在編寫繼承自UIView的自定義組件時,也想要有Intrinsic Content Size的時候验庙,就可以通過這種方法來輕松實現(xiàn)顶吮。

在日常開發(fā)中,我們發(fā)現(xiàn)給 UILabel粪薛、UIImageView悴了、UIButton 實例寫約束的時候,只需要給他們位置违寿,而不需要給大小湃交。這是因為它們指定了 intrinsicContentSize(可以理解為內(nèi)部通過內(nèi)容計算出了一個合理的大小,我們可以不用指定它)藤巢。

Content Hugging (不想變大約束):如果組件的此屬性優(yōu)先級比另一個組件此屬性優(yōu)先級高的話搞莺,那么這個組件就保持不變,另一個可以在需要拉伸的時候拉伸掂咒。

Content Compression (不想變小約束):如果組件的此屬性優(yōu)先級比另一個組件此屬性優(yōu)先級高的話才沧,那么這個組件就保持不變,另一個可以在需要壓縮的時候壓縮绍刮。

Content Hugging/Content Compression

Content Hugging温圆,Content Compression是兩個UIView自帶的模糊約束,而這兩個約束存在的條件則是UIView必須指定了 Intrinsic Content Size孩革。

Intrinsic沖突 – 一個UIView有了 Intrinsic Content Size 之后岁歉,才可以只指定位置,而不用指定大小膝蜈。并且才可能會觸發(fā)上述兩個約束锅移。 但是問題又來了,對于上述這種UIView來說彬檀,只指定位置而不指定大小帆啃,有的時候會有問題。 我們用UILabel來舉例吧(所有支持Intrinsic Content Size 的組件都有此問題)窍帝。 2個UILabel努潘,UILabel1(文字內(nèi)容:UILabel1)和UILabel2(文字內(nèi)容:UILabel2),其內(nèi)容按照下面說明布局: - 2個UILabel距離上邊欄為50點坤学。 - UILabel1與左邊欄距離為10疯坤,UILabel2左面距離UILabel1為10點。 因為都具有Intrinsic屬性深浮,所以不需要指定size压怠。位置應(yīng)該也明確了

現(xiàn)在問題來了,再給UILabel2加一條約束飞苇,右側(cè)距離右邊欄為10點菌瘫。

很明顯蜗顽,如果按照約束來布局,則沒辦法滿足2個UIlabel都使用 Intrinsic Content Size雨让,至少某個UILabel的寬度大于Intrinsic Content Size雇盖。這種情況,我們稱之為2個組件之間的“Intrinsic沖突”

解決“Intrinsic沖突”的方案有2種:

兩個UIlabel都不使用Intrinsic Content Size栖忠。為兩個UIlabel增加新的約束崔挖,來顯式指定它們的大小。如:給2個UIlabel增加寬度和高度約束或等寬等高約束等等

可以讓其中一個UIlabel使用Intrinsic Content Size庵寞,另一個label則自動占用剩余的空間狸相。這時候就需要用到 Content Hugging 和 Content Compression Resistance了!具體做法在下面介紹捐川。

一句話總結(jié)“Intrinsic沖突”:兩個或多個可以使用Intrinsic Content Size的組件脓鹃,因為組件中添加的其他約束,而無法同時使用 intrinsic Content Size了属拾。

content Hugging/content Compression Resistance – 首先将谊,這兩個概念都是UIView的屬性。 假設(shè)兩個組件產(chǎn)生了“Intrinsic沖突”: 1. Content Hugging 約束(不想變大約束)表示:如果組件的此屬性優(yōu)先級比另一個組件此屬性優(yōu)先級高的話渐白,那么這個組件就保持不變,另一個可以在需要拉伸的時候拉伸逞频。屬性分橫向和縱向2個方向纯衍。 2. Content Compression Resistance 約束(不想變小約束)表示:如果組件的此屬性優(yōu)先級比另一個組件此屬性優(yōu)先級高的話,那么這個組件就保持不變苗胀,另一個可以在需要壓縮的時候壓縮襟诸。屬性分橫向和縱向2個方向。 意思很明顯基协。上面UIlabel這個例子中歌亲,很顯然,如果某個UILabel使用Intrinsic Content Size的時候澜驮,另一個需要拉伸陷揪。 所以我們需要調(diào)整兩個UILabel的 Content Hugging約束的優(yōu)先級就可以啦。 在這個頁面可以調(diào)整優(yōu)先級(拉到最下面)杂穷。

分別調(diào)整兩個UILabel的 Content Hugging的優(yōu)先級可以得到不同的結(jié)果:

Content Compression Resistance 的情況就不多說了悍缠,原理相同。

2021年7月2日補充:

抗壓縮(不想變小約束)

setContentCompressionResistancePriority

抗拉伸(不想變大約束)

setContentHuggingPriority

約束優(yōu)先級: 在 Autolayout 中每個約束都有一個優(yōu)先級, 優(yōu)先級的范圍是1 ~ 1000耐量。創(chuàng)建一個約束飞蚓,默認(rèn)的優(yōu)先級是最高的1000

Content Hugging Priority: 該優(yōu)先級表示一個控件抗被拉伸的優(yōu)先級。優(yōu)先級越高廊蜒,越不容易被拉伸趴拧,默認(rèn)是250溅漾。

Content Compression Resistance Priority: 該優(yōu)先級和上面那個優(yōu)先級相對應(yīng),表示一個控件抗壓縮的優(yōu)先級著榴。優(yōu)先級越高添履,越不容易被壓縮,默認(rèn)是750兄渺。

UIView?提供了兩個方法給我們設(shè)置它的這兩個約束的優(yōu)先級:

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

其中缝龄,第一個參數(shù)為優(yōu)先級,具體有以下幾種:

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)先級叔壤。

第二個參數(shù)代表你要設(shè)置約束優(yōu)先級的方向,分為橫向和縱向:

typedef NS_ENUM(NSInteger, UILayoutConstraintAxis) {

UILayoutConstraintAxisHorizontal = 0,

UILayoutConstraintAxisVertical = 1

}

UILayoutConstraintAxisHorizontal:橫向

UILayoutConstraintAxisVertical:縱向

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末口叙,一起剝皮案震驚了整個濱河市炼绘,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌妄田,老刑警劉巖俺亮,帶你破解...
    沈念sama閱讀 206,602評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異疟呐,居然都是意外死亡脚曾,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,442評論 2 382
  • 文/潘曉璐 我一進店門启具,熙熙樓的掌柜王于貴愁眉苦臉地迎上來本讥,“玉大人,你說我怎么就攤上這事鲁冯】椒校” “怎么了?”我有些...
    開封第一講書人閱讀 152,878評論 0 344
  • 文/不壞的土叔 我叫張陵薯演,是天一觀的道長撞芍。 經(jīng)常有香客問我,道長跨扮,這世上最難降的妖魔是什么序无? 我笑而不...
    開封第一講書人閱讀 55,306評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮好港,結(jié)果婚禮上愉镰,老公的妹妹穿的比我還像新娘。我一直安慰自己钧汹,他們只是感情好丈探,可當(dāng)我...
    茶點故事閱讀 64,330評論 5 373
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著拔莱,像睡著了一般碗降。 火紅的嫁衣襯著肌膚如雪隘竭。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,071評論 1 285
  • 那天讼渊,我揣著相機與錄音动看,去河邊找鬼。 笑死爪幻,一個胖子當(dāng)著我的面吹牛菱皆,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播挨稿,決...
    沈念sama閱讀 38,382評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼仇轻,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了奶甘?” 一聲冷哼從身側(cè)響起篷店,我...
    開封第一講書人閱讀 37,006評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎臭家,沒想到半個月后疲陕,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,512評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡钉赁,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,965評論 2 325
  • 正文 我和宋清朗相戀三年蹄殃,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片你踩。...
    茶點故事閱讀 38,094評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡窃爷,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出姓蜂,到底是詐尸還是另有隱情,我是刑警寧澤医吊,帶...
    沈念sama閱讀 33,732評論 4 323
  • 正文 年R本政府宣布钱慢,位于F島的核電站,受9級特大地震影響卿堂,放射性物質(zhì)發(fā)生泄漏束莫。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,283評論 3 307
  • 文/蒙蒙 一草描、第九天 我趴在偏房一處隱蔽的房頂上張望览绿。 院中可真熱鬧,春花似錦穗慕、人聲如沸饿敲。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,286評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽怀各。三九已至倔韭,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間瓢对,已是汗流浹背寿酌。 一陣腳步聲響...
    開封第一講書人閱讀 31,512評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留硕蛹,地道東北人醇疼。 一個月前我還...
    沈念sama閱讀 45,536評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像法焰,于是被迫代替她去往敵國和親秧荆。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,828評論 2 345

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