在了解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
代碼效果如下: