在了解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:縱向