在瞭解intrinsicContentSize以前,咱們須要先了解2個概念:bash
若是不瞭解這兩個概念,看intinsic content size沒有任何意義。
注:因爲上面這幾個概念都是針對UIView或其子類(UILabel,UIImageView等等)來講的。因此下文中都用UIView指代。app
AutoLayout在作什麼 – 一個UIView想要顯示在屏幕中,僅須有2個須要肯定的元素,一是位置,二是大小。只要2者肯定,UIView就能夠正確顯示,至於顯示的內容,則由UIView本身決定(drawRect)。iview
沒有AutoLayout的時候,咱們須要經過 initWithFrame:(CGRect)這種方式來指定UIView的位置和大小。函數
而使用AutoLayout的過程,就是經過約束來肯定UIView的位置和大小的過程。佈局
約束優先級 – 爲何約束須要優先級?由於有的時候2個約束可能會有衝突。 好比:有一個UIView 距離父UIView的左右距離都是0,這是2個約束,此時再給此UIView加一個寬度約束,好比指定寬度爲100,那麼就會產生約束衝突了。學習
由於,這兩種約束不可能同時存在,只能知足一個,那麼知足誰呢?默認狀況下給UIView加的這幾個約束優先級都是1000,屬於最高的優先級了,表示此約束必須知足。測試
因此這種衝突不能被iOS所容許。此時就須要修改優先級了。把其中任意一個約束的優先級改成小於1000的值便可。字體
iOS能夠經過比較兩個」相互衝突的約束」的優先級,從而忽略低優先級的某個約束,達到正確佈局的目的。ui
用鼠標選中寬度約束,而後在屏幕右側的菜單中,修改優先級,以下圖:this
這樣就沒有約束衝突了。由於若是一旦兩個約束衝突,系統會自動忽略優先級低的約束。
上面舉的這個例子有些極端,由於上面兩個約束都是肯定的值,並且是絕對衝突。因此若是遇到這種狀況,可能選擇刪掉某個約束更爲合適。
而約束優先級更多的時候用於解決模糊約束(相對於上面的肯定值約束來講)的衝突的問題。
好比有這樣一個問題:
這是一個很普通的應用場景,假設我但願有這樣一個效果: 我但願UIView2的寬度不能超過50。當UIView1寬度小於50的時候,兩者等寬;當UIView1寬度大於50的時候,UIView2不受UIView1寬度的影響。
因而我給UIView2加上一條約束:寬度<=50。這時候衝突來了:
由於UIView1的寬度是定好的,而UIView2和UIView1等寬。那麼UIView2的寬度就是肯定的。
很顯然,分爲兩種狀況(根據UIView1的寬度不一樣):
更糟糕的是,實際狀況中,UIView1的寬度可能不是一個肯定的值。它有可能會被頁面中的其餘View所影響,可能還會在運行時產生變化,並不能保證它的實際寬度必定小於50。因此,一旦產生約束衝突,可能就會對應用產生不肯定的影響:可能顯示錯亂,也可能程序崩潰。
因此咱們爲了獲得正確的結果,應該這樣處理:
因此咱們把等寬約束的優先級修改成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是不用指定尺寸大小的,只需指定位置便可,就是由於,只要肯定了文字內容,字體等信息,它本身就能計算出大小來。
UILabel,UIImageView,UIButton等這些組件及某些包含它們的系統組件都有 Intrinsic Content Size 屬性。
也就是說,遇到這些組件,你只須要爲其指定位置便可。大小就使用Intrinsic Content Size就好了。
在代碼中,上述系統控件都重寫了UIView 中的 -(CGSize)intrinsicContentSize: 方法。
而且在須要改變這個值的時候調用:invalidateIntrinsicContentSize 方法,通知系統這個值改變了。
因此當咱們在編寫繼承自UIView的自定義組件時,也想要有Intrinsic Content Size的時候,就能夠經過這種方法來輕鬆實現。
Intrinsic衝突 – 一個UIView有了 Intrinsic Content Size 以後,才能夠只指定位置,而不用指定大小。而且纔可能會觸發上述兩個約束。 可是問題又來了,對於上述這種UIView來講,只指定位置而不指定大小,有的時候會有問題。 咱們用UILabel來舉例吧(全部支持Intrinsic Content Size 的組件都有此問題)。 2個UILabel,UILabel1(文字內容:UILabel1)和UILabel2(文字內容:UILabel2),其內容按照下面說明佈局: - 2個UILabel距離上邊欄爲50點。 - UILabel1與左邊欄距離爲10,UILabel2左面距離UILabel1爲10點。 由於都具備Intrinsic屬性,因此不須要指定size。位置應該也明確了。
如今問題來了,再給UILabel2加一條約束,右側距離右邊欄爲10點。
很明顯,若是按照約束來佈局,則沒辦法知足2個UIlabel都使用 Intrinsic Content Size,至少某個UILabel的寬度大於Intrinsic Content Size。這種狀況,咱們稱之爲2個組件之間的「Intrinsic衝突」。
解決「Intrinsic衝突」的方案有2種:
一句話總結「Intrinsic衝突」:兩個或多個可使用Intrinsic Content Size的組件,由於組件中添加的其餘約束,而沒法同時使用 intrinsic Content Size了。
content Hugging/content Compression Resistance – 首先,這兩個概念都是UIView的屬性。 假設兩個組件產生了「Intrinsic衝突」: 1. Content Hugging 約束(不想變大約束)表示:若是組件的此屬性優先級比另外一個組件此屬性優先級高的話,那麼這個組件就保持不變,另外一個能夠在須要拉伸的時候拉伸。屬性分橫向和縱向2個方向。 2. Content Compression Resistance 約束(不想變小約束)表示:若是組件的此屬性優先級比另外一個組件此屬性優先級高的話,那麼這個組件就保持不變,另外一個能夠在須要壓縮的時候壓縮。屬性分橫向和縱向2個方向。 意思很明顯。上面UIlabel這個例子中,很顯然,若是某個UILabel使用Intrinsic Content Size的時候,另外一個須要拉伸。 因此咱們須要調整兩個UILabel的 Content Hugging約束的優先級就能夠啦。 在這個頁面能夠調整優先級(拉到最下面)。
分別調整兩個UILabel的 Content Hugging的優先級能夠獲得不一樣的結果:
Content Compression Resistance 的狀況就很少說了,原理相同。
[label setContentHuggingPriority:UILayoutPriorityDefaultHigh forAxis:UILayoutConstraintAxisHorizontal]; [label setContentCompressionResistancePriority: UILayoutPriorityDefaultHigh forAxis:UILayoutConstraintAxisHorizontal];
Priority是個enum:
typedef float UILayoutPriority;
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; // When you send -[UIView systemLayoutSizeFittingSize:], the size fitting most closely to the target size (the argument) is computed. UILayoutPriorityFittingSizeLevel is the priority level with which the view wants to conform to the target size in that computation. It's quite low. It is generally not appropriate to make a constraint at exactly this priority. You want to be higher or lower.
Axis表示橫向及縱向:
typedef NS_ENUM(NSInteger, UILayoutConstraintAxis) { UILayoutConstraintAxisHorizontal = 0, UILayoutConstraintAxisVertical = 1 };
代碼及註釋以下:
//IntrinsicView.h #import <UIKit/UIKit.h> @interface IntrinsicView : UIView @property (nonatomic) CGSize extendSize; @end
//IntrinsicView.m #import "IntrinsicView.h" static bool closeIntrinsic = false;//測試關閉Intrinsic的影響 @implementation IntrinsicView - (instancetype)init { self = [super init]; if (self) { //不兼容舊版Autoreizingmask,只使用AutoLayout //若是爲YES,在AutoLayout中則會自動將view的frame和bounds屬性轉換爲約束。 self.translatesAutoresizingMaskIntoConstraints = NO; } return self; } //當用戶設置extendSize時,提示系統IntrinsicContentSize變化了。 -(void)setExtendSize:(CGSize)extendSize{ _extendSize = extendSize; //若是不加這句話,在view顯示以後(好比延時幾秒),再設置extendSize不會有效果。 //本例中也就是testInvalidateIntrinsic的方法不會產生預期效果。 [self invalidateIntrinsicContentSize]; } //經過覆蓋intrinsicContentSize函數修改View的Intrinsic的大小 -(CGSize)intrinsicContentSize{ if (closeIntrinsic) { return CGSizeMake(UIViewNoIntrinsicMetric, UIViewNoIntrinsicMetric); } else { return CGSizeMake(_extendSize.width, _extendSize.height); } } @end
//測試代碼 #import "ViewController.h" #import "newViewCtlViewController.h" #import "IntrinsicView.h" @interface ViewController () @end @implementation ViewController -(void)viewDidLoad{ [super viewDidLoad]; [self testIntrinsicView]; }+ -(void) testIntrinsicView{ IntrinsicView *intrinsicView1 = [[IntrinsicView alloc] init]; intrinsicView1.extendSize = CGSizeMake(100, 100); intrinsicView1.backgroundColor = [UIColor greenColor]; [self.view addSubview:intrinsicView1]; [self.view addConstraints:@[ //距離superview上方100點 [NSLayoutConstraint constraintWithItem:intrinsicView1 attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeTop multiplier:1 constant:100], //距離superview左面10點 [NSLayoutConstraint constraintWithItem:intrinsicView1 attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeLeft multiplier:1 constant:10], ]]; IntrinsicView *intrinsicView2 = [[IntrinsicView alloc] init]; intrinsicView2.extendSize = CGSizeMake(100, 30); intrinsicView2.backgroundColor = [UIColor redColor]; [self.view addSubview:intrinsicView2]; [self.view addConstraints:@[ //距離superview上方220點 [NSLayoutConstraint constraintWithItem:intrinsicView2 attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeTop multiplier:1 constant:220], //距離superview左面10點 [NSLayoutConstraint constraintWithItem:intrinsicView2 attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeLeft multiplier:1 constant:10], ]]; [self performSelector:@selector(testInvalidateIntrinsic:) withObject:intrinsicView2 afterDelay:2]; } -(void) testInvalidateIntrinsic:(IntrinsicView *)view{ view.extendSize = CGSizeMake(100, 80); } @end
代碼效果以下: