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

在了解intrinsicContentSize之前秧秉,我們需要先了解2個(gè)概念:

AutoLayout在做什么

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

如果不了解這兩個(gè)概念衰抑,看intinsic content size沒(méi)有任何意義象迎。?

注:由于上面這幾個(gè)概念都是針對(duì)UIView或其子類(lèi)(UILabel,UIImageView等等)來(lái)說(shuō)的呛踊。所以下文中都用UIView指代砾淌。

AutoLayout在做什么 – 一個(gè)UIView想要顯示在屏幕中完丽,僅須有2個(gè)需要確定的元素,一是位置拇舀,二是大小。只要2者確定蜻底,UIView就可以正確顯示骄崩,至于顯示的內(nèi)容,則由UIView自己決定(drawRect)薄辅。

沒(méi)有AutoLayout的時(shí)候要拂,我們需要通過(guò) initWithFrame:(CGRect)這種方式來(lái)指定UIView的位置和大小。

而使用AutoLayout的過(guò)程站楚,就是通過(guò)約束來(lái)確定UIView的位置和大小的過(guò)程脱惰。

約束優(yōu)先級(jí) – 為什么約束需要優(yōu)先級(jí)?因?yàn)橛械臅r(shí)候2個(gè)約束可能會(huì)有沖突窿春。 比如:有一個(gè)UIView 距離父UIView的左右距離都是0拉一,這是2個(gè)約束,此時(shí)再給此UIView加一個(gè)寬度約束旧乞,比如指定寬度為100蔚润,那么就會(huì)產(chǎn)生約束沖突了。

因?yàn)槌咂埽@兩種約束不可能同時(shí)存在嫡纠,只能滿(mǎn)足一個(gè),那么滿(mǎn)足誰(shuí)呢延赌?默認(rèn)情況下給UIView加的這幾個(gè)約束優(yōu)先級(jí)都是1000除盏,屬于最高的優(yōu)先級(jí)了,表示此約束必須滿(mǎn)足挫以。

所以這種沖突不能被iOS所允許者蠕。此時(shí)就需要修改優(yōu)先級(jí)了。把其中任意一個(gè)約束的優(yōu)先級(jí)改為小于1000的值即可屡贺。

iOS可以通過(guò)比較兩個(gè)”相互沖突的約束”的優(yōu)先級(jí)蠢棱,從而忽略低優(yōu)先級(jí)的某個(gè)約束,達(dá)到正確布局的目的甩栈。

用鼠標(biāo)選中寬度約束泻仙,然后在屏幕右側(cè)的菜單中,修改優(yōu)先級(jí)量没,如下圖:

這樣就沒(méi)有約束沖突了玉转。因?yàn)槿绻坏﹥蓚€(gè)約束沖突,系統(tǒng)會(huì)自動(dòng)忽略?xún)?yōu)先級(jí)低的約束殴蹄。

上面舉的這個(gè)例子有些極端究抓,因?yàn)樯厦鎯蓚€(gè)約束都是確定的值猾担,而且是絕對(duì)沖突。所以如果遇到這種情況刺下,可能選擇刪掉某個(gè)約束更為合適绑嘹。

而約束優(yōu)先級(jí)更多的時(shí)候用于解決模糊約束(相對(duì)于上面的確定值約束來(lái)說(shuō))的沖突的問(wèn)題。?

比如有這樣一個(gè)問(wèn)題:

UIView1有四個(gè)約束:距離父UIView左和上確定橘茉,寬和高也確定工腋。

UIView2在UIView1的下面,約束也有4個(gè):上面距離UIView1確定畅卓,左面同UIView1對(duì)齊擅腰,同UIView1等高且等寬。?

此時(shí)這兩個(gè)UIView應(yīng)該像這樣:

這是一個(gè)很普通的應(yīng)用場(chǎng)景翁潘,假設(shè)我希望有這樣一個(gè)效果: 我希望UIView2的寬度不能超過(guò)50趁冈。當(dāng)UIView1寬度小于50的時(shí)候,二者等寬拜马;當(dāng)UIView1寬度大于50的時(shí)候渗勘,UIView2不受UIView1寬度的影響。?

于是我給UIView2加上一條約束:寬度<=50一膨。這時(shí)候沖突來(lái)了:?

因?yàn)閁IView1的寬度是定好的呀邢,而UIView2和UIView1等寬。那么UIView2的寬度就是確定的豹绪。

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

若UIView1的寬度大于50,UIView2的寬度也一定大于50瞒津,這跟新加的限制寬度<=50的約束是沖突的蝉衣。

否則不沖突。

更糟糕的是巷蚪,實(shí)際情況中病毡,UIView1的寬度可能不是一個(gè)確定的值。它有可能會(huì)被頁(yè)面中的其他View所影響屁柏,可能還會(huì)在運(yùn)行時(shí)產(chǎn)生變化啦膜,并不能保證它的實(shí)際寬度一定小于50。所以淌喻,一旦產(chǎn)生約束沖突筋遭,可能就會(huì)對(duì)應(yīng)用產(chǎn)生不確定的影響:可能顯示錯(cuò)亂屯掖,也可能程序崩潰涯穷。

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

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

當(dāng)UIView1寬度大于50的時(shí)候清蚀,忽略等寬約束,也就是降低等寬約束優(yōu)先級(jí)爹谭。

所以我們把等寬約束的優(yōu)先級(jí)修改為999枷邪。上面兩條都滿(mǎn)足,問(wèn)題解決诺凡。

說(shuō)到模糊約束齿风,content Hugging/content Compression Resistance就是2個(gè)UIView自帶的模糊約束。?

而這兩個(gè)約束存在的條件則是UIView必須指定了 Intrinsic Content Size绑洛。?

在了解這兩個(gè)模糊約束之前,必須了解Intrinsic Content Size是什么東西童本。

Intrinsic Contenet Size – Intrinsic Content Size:固有大小真屯。顧名思義,在AutoLayout中穷娱,它作為UIView的屬性(不是語(yǔ)法上的屬性)绑蔫,意思就是說(shuō)我知道自己的大小,如果你沒(méi)有為我指定大小泵额,我就按照這個(gè)大小來(lái)配深。 比如:大家都知道在使用AutoLayout的時(shí)候,UILabel是不用指定尺寸大小的嫁盲,只需指定位置即可篓叶,就是因?yàn)椋灰_定了文字內(nèi)容羞秤,字體等信息缸托,它自己就能計(jì)算出大小來(lái)。

UILabel瘾蛋,UIImageView俐镐,UIButton等這些組件及某些包含它們的系統(tǒng)組件都有 Intrinsic Content Size 屬性。?

也就是說(shuō)哺哼,遇到這些組件佩抹,你只需要為其指定位置即可。大小就使用Intrinsic Content Size就行了取董。

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

并且在需要改變這個(gè)值的時(shí)候調(diào)用:invalidateIntrinsicContentSize 方法甲葬,通知系統(tǒng)這個(gè)值改變了廊勃。

所以當(dāng)我們?cè)诰帉?xiě)繼承自UIView的自定義組件時(shí),也想要有Intrinsic Content Size的時(shí)候,就可以通過(guò)這種方法來(lái)輕松實(shí)現(xiàn)坡垫。

Intrinsic沖突 – 一個(gè)UIView有了 Intrinsic Content Size 之后梭灿,才可以只指定位置,而不用指定大小冰悠。并且才可能會(huì)觸發(fā)上述兩個(gè)約束堡妒。 但是問(wèn)題又來(lái)了,對(duì)于上述這種UIView來(lái)說(shuō)溉卓,只指定位置而不指定大小皮迟,有的時(shí)候會(huì)有問(wèn)題。 我們用UILabel來(lái)舉例吧(所有支持Intrinsic Content Size 的組件都有此問(wèn)題)桑寨。 2個(gè)UILabel伏尼,UILabel1(文字內(nèi)容:UILabel1)和UILabel2(文字內(nèi)容:UILabel2),其內(nèi)容按照下面說(shuō)明布局: - 2個(gè)UILabel距離上邊欄為50點(diǎn)尉尾。 - UILabel1與左邊欄距離為10爆阶,UILabel2左面距離UILabel1為10點(diǎn)。 因?yàn)槎季哂蠭ntrinsic屬性沙咏,所以不需要指定size辨图。位置應(yīng)該也明確了。

現(xiàn)在問(wèn)題來(lái)了肢藐,再給UILabel2加一條約束故河,右側(cè)距離右邊欄為10點(diǎn)。

很明顯吆豹,如果按照約束來(lái)布局鱼的,則沒(méi)辦法滿(mǎn)足2個(gè)UIlabel都使用 Intrinsic Content Size,至少某個(gè)UILabel的寬度大于Intrinsic Content Size痘煤。這種情況鸳吸,我們稱(chēng)之為2個(gè)組件之間的“Intrinsic沖突”。

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

兩個(gè)UIlabel都不使用Intrinsic Content Size速勇。為兩個(gè)UIlabel增加新的約束晌砾,來(lái)顯式指定它們的大小。如:給2個(gè)UIlabel增加寬度和高度約束或等寬等高約束等等烦磁。

可以讓其中一個(gè)UIlabel使用Intrinsic Content Size养匈,另一個(gè)label則自動(dòng)占用剩余的空間。這時(shí)候就需要用到 Content Hugging 和 Content Compression Resistance了都伪!具體做法在下面介紹呕乎。

一句話(huà)總結(jié)“Intrinsic沖突”:兩個(gè)或多個(gè)可以使用Intrinsic Content Size的組件,因?yàn)榻M件中添加的其他約束陨晶,而無(wú)法同時(shí)使用 intrinsic Content Size了猬仁。

content Hugging/content Compression Resistance – 首先帝璧,這兩個(gè)概念都是UIView的屬性。 假設(shè)兩個(gè)組件產(chǎn)生了“Intrinsic沖突”: 1. Content Hugging 約束(不想變大約束)表示:如果組件的此屬性?xún)?yōu)先級(jí)比另一個(gè)組件此屬性?xún)?yōu)先級(jí)高的話(huà)湿刽,那么這個(gè)組件就保持不變的烁,另一個(gè)可以在需要拉伸的時(shí)候拉伸。屬性分橫向和縱向2個(gè)方向诈闺。 2. Content Compression Resistance 約束(不想變小約束)表示:如果組件的此屬性?xún)?yōu)先級(jí)比另一個(gè)組件此屬性?xún)?yōu)先級(jí)高的話(huà)渴庆,那么這個(gè)組件就保持不變,另一個(gè)可以在需要壓縮的時(shí)候壓縮雅镊。屬性分橫向和縱向2個(gè)方向襟雷。 意思很明顯。上面UIlabel這個(gè)例子中仁烹,很顯然耸弄,如果某個(gè)UILabel使用Intrinsic Content Size的時(shí)候,另一個(gè)需要拉伸卓缰。 所以我們需要調(diào)整兩個(gè)UILabel的 Content Hugging約束的優(yōu)先級(jí)就可以啦叙赚。 在這個(gè)頁(yè)面可以調(diào)整優(yōu)先級(jí)(拉到最下面)。

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

Content Compression Resistance 的情況就不多說(shuō)了僚饭,原理相同。

在代碼中修改UIView的這兩個(gè)優(yōu)先級(jí)

[labelsetContentHuggingPriority:UILayoutPriorityDefaultHighforAxis:UILayoutConstraintAxisHorizontal];? ? [labelsetContentCompressionResistancePriority: UILayoutPriorityDefaultHighforAxis:UILayoutConstraintAxisHorizontal];

1

2

Priority是個(gè)enum:

typedef float UILayoutPriority;static const UILayoutPriority UILayoutPriorityRequired NS_AVAILABLE_IOS(6_0) =1000; // A required constraint.? Donotexceed this.static const UILayoutPriority UILayoutPriorityDefaultHigh NS_AVAILABLE_IOS(6_0) =750; // Thisisthe priority levelwithwhich a button resists compressing its content.static const UILayoutPriority UILayoutPriorityDefaultLow NS_AVAILABLE_IOS(6_0) =250; // Thisisthe priority level at which a button hugs its contents horizontally.static const UILayoutPriority UILayoutPriorityFittingSizeLevel NS_AVAILABLE_IOS(6_0) =50; //Whenyou send -[UIView systemLayoutSizeFittingSize:], the size fitting most closelytothe target size (the argument)iscomputed.? UILayoutPriorityFittingSizeLevelisthe priority levelwithwhich the view wantstoconformtothe target sizeinthat computation.? It'squite low.? Itisgenerallynotappropriatetomake a constraint at exactly this priority.? You wanttobe higherorlower.

1

2

3

4

5

Axis表示橫向及縱向:

typedefNS_ENUM(NSInteger, UILayoutConstraintAxis) {? ? UILayoutConstraintAxisHorizontal =0,? ? UILayoutConstraintAxisVertical =1};

1

2

3

4

創(chuàng)建自定義具有 Intrinsic Content Size 功能的組件

代碼及注釋如下:

//IntrinsicView.h#import@interfaceIntrinsicView:UIView@property(nonatomic)CGSizeextendSize;@end

1

2

3

4

5

6

//IntrinsicView.m#import"IntrinsicView.h"staticboolcloseIntrinsic =false;//測(cè)試關(guān)閉Intrinsic的影響@implementationIntrinsicView- (instancetype)init{self= [superinit];if(self) {//不兼容舊版Autoreizingmask胧砰,只使用AutoLayout//如果為YES鳍鸵,在AutoLayout中則會(huì)自動(dòng)將view的frame和bounds屬性轉(zhuǎn)換為約束。self.translatesAutoresizingMaskIntoConstraints=NO;? ? }returnself;}//當(dāng)用戶(hù)設(shè)置extendSize時(shí)尉间,提示系統(tǒng)IntrinsicContentSize變化了偿乖。-(void)setExtendSize:(CGSize)extendSize{? ? _extendSize = extendSize;//如果不加這句話(huà),在view顯示之后(比如延時(shí)幾秒)哲嘲,再設(shè)置extendSize不會(huì)有效果贪薪。//本例中也就是testInvalidateIntrinsic的方法不會(huì)產(chǎn)生預(yù)期效果。[selfinvalidateIntrinsicContentSize];}//通過(guò)覆蓋intrinsicContentSize函數(shù)修改View的Intrinsic的大小-(CGSize)intrinsicContentSize{if(closeIntrinsic) {returnCGSizeMake(UIViewNoIntrinsicMetric, UIViewNoIntrinsicMetric);? ? }else{returnCGSizeMake(_extendSize.width, _extendSize.height);? ? }}@end

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

//測(cè)試代碼#import"ViewController.h"#import"newViewCtlViewController.h"#import"IntrinsicView.h"@interfaceViewController()@end@implementationViewController-(void)viewDidLoad{? ? [superviewDidLoad];? ? [selftestIntrinsicView];}+-(void) testIntrinsicView{? ? IntrinsicView *intrinsicView1 = [[IntrinsicView alloc] init];? ? intrinsicView1.extendSize= CGSizeMake(100,100);? ? intrinsicView1.backgroundColor= [UIColorgreenColor];? ? [self.viewaddSubview:intrinsicView1];? ? [self.viewaddConstraints:@[//距離superview上方100點(diǎn)[NSLayoutConstraint constraintWithItem:intrinsicView1 attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.viewattribute:NSLayoutAttributeTop multiplier:1constant:100],//距離superview左面10點(diǎn)[NSLayoutConstraint constraintWithItem:intrinsicView1 attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self.viewattribute:NSLayoutAttributeLeft multiplier:1constant:10],? ? ]];? ? IntrinsicView *intrinsicView2 = [[IntrinsicView alloc] init];? ? intrinsicView2.extendSize= CGSizeMake(100,30);? ? intrinsicView2.backgroundColor= [UIColorredColor];? ? [self.viewaddSubview:intrinsicView2];? ? [self.viewaddConstraints:@[//距離superview上方220點(diǎn)[NSLayoutConstraint constraintWithItem:intrinsicView2 attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.viewattribute:NSLayoutAttributeTop multiplier:1constant:220],//距離superview左面10點(diǎn)[NSLayoutConstraint constraintWithItem:intrinsicView2 attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self.viewattribute:NSLayoutAttributeLeft multiplier:1constant:10],? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ]];? ? [selfperformSelector:@selector(testInvalidateIntrinsic:) withObject:intrinsicView2 afterDelay:2];}-(void) testInvalidateIntrinsic:(IntrinsicView *)view{? ? view.extendSize= CGSizeMake(100,80);}@end

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

代碼效果如下:

?著作權(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)店門(mén)台丛,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)耍缴,“玉大人,你說(shuō)我怎么就攤上這事》牢耍” “怎么了变汪?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,282評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)本鸣。 經(jīng)常有香客問(wèn)我疫衩,道長(zhǎng),這世上最難降的妖魔是什么荣德? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,842評(píng)論 1 295
  • 正文 為了忘掉前任闷煤,我火速辦了婚禮,結(jié)果婚禮上涮瞻,老公的妹妹穿的比我還像新娘鲤拿。我一直安慰自己,他們只是感情好署咽,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,857評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布近顷。 她就那樣靜靜地躺著,像睡著了一般宁否。 火紅的嫁衣襯著肌膚如雪窒升。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,679評(píng)論 1 305
  • 那天慕匠,我揣著相機(jī)與錄音饱须,去河邊找鬼。 笑死台谊,一個(gè)胖子當(dāng)著我的面吹牛蓉媳,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播锅铅,決...
    沈念sama閱讀 40,406評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼酪呻,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了盐须?” 一聲冷哼從身側(cè)響起玩荠,我...
    開(kāi)封第一講書(shū)人閱讀 39,311評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎贼邓,沒(méi)想到半個(gè)月后姨蟋,有當(dāng)?shù)厝嗽跇?shù)林里發(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
  • 文/蒙蒙 一毛甲、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧具被,春花似錦玻募、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,988評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至叮叹,卻和暖如春艾栋,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背蛉顽。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,101評(píng)論 1 271
  • 我被黑心中介騙來(lái)泰國(guó)打工蝗砾, 沒(méi)想到剛下飛機(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)容