iOS自動(dòng)布局
1 autoresizing
autoresizingMask:創(chuàng)建視圖的同時(shí)給出其相對(duì)于父視圖的“對(duì)齊方式與縮放系數(shù)”。當(dāng)父視圖發(fā)生變化時(shí),通過每個(gè)子視圖的autoresizingMask即可自動(dòng)得出新的位置,而無需開發(fā)者提供。
缺點(diǎn):
- 其描述界面變化規(guī)則不夠靈活,很多變化規(guī)則根本無法精確描述。autoresizingMask縮放比例是UIKit內(nèi)部計(jì)算的,開發(fā)者無法指定縮放比例的精確值。
- 變化規(guī)則只能基于父視圖與子視圖之間,無法建立同級(jí)視圖或者跨級(jí)視圖之間的關(guān)系。
樣例:
- (void)viewDidLoad {
[super viewDidLoad];
UIView *containerView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 200)];
containerView.backgroundColor = [UIColor blueColor];
[self.view addSubview:containerView];
UILabel *text = [[UILabel alloc] initWithFrame:CGRectZero];
text.text = @"1231312";
[containerView addSubview:text];
text.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleWidth;
text.frame = CGRectMake(0, 0, containerView.bounds.size.width - 20, 100);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
containerView.frame = CGRectMake(0, 0, 300, 200);
NSLog(@"%@ %@ %@ %@", @(text.frame.origin.x), @(text.frame.origin.y), @(text.frame.size.width),
@(text.frame.size.height));
});
}
2 autolayout
autolayout:它允許開發(fā)者在界面上的任意兩個(gè)視圖之間建立精確的線性變化規(guī)則。所謂線性變化就是數(shù)學(xué)中的一次函數(shù),即:y = m*x + c,其中x和y是界面中任意兩個(gè)視圖的某個(gè)布局屬性,m為比例系數(shù),c為常量。每個(gè)線性變化規(guī)則稱之為布局約束(Layout Constraint)。
2.1 NSLayoutConstraint
NS_CLASS_AVAILABLE_IOS(6_0)
@interface NSLayoutConstraint : NSObject
...
@property (readonly, assign) id firstItem;
@property (readonly) NSLayoutAttribute firstAttribute;
@property (readonly) NSLayoutRelation relation;
@property (readonly, assign) id secondItem;
@property (readonly) NSLayoutAttribute secondAttribute;
@property (readonly) CGFloat multiplier;
@property CGFloat constant;
...
+(instancetype)constraintWithItem:(id)firstItem attribute:(NSLayoutAttribute)firstAttribute
relatedBy:(NSLayoutRelation)relation
toItem:(id)secondItem attribute:(NSLayoutAttribute)secondAttribute
multiplier:(CGFloat)multiplier constant:(CGFloat)constant;
公式:firstItem.firstAttribute {==,<=,>=} secondItem.secondAttribute * multiplier + constant
解釋:firstItem與secondItem分別是界面中受約束的視圖與被參照的視圖。他們不一定非得是兄弟關(guān)系或者父子關(guān)系,只要是他們有著共同的祖先視圖即可,這一點(diǎn)是autoresizingMask無法做到的。
fir
stAttribute與secondAttribute分別是firstItem與secondItem的某個(gè)布局屬性(NSLayoutAttribute)。注意,firstItem與secondItem不一定非得是同樣的值,允許定義諸如某視圖的高度等于另一個(gè)視圖的寬度這樣的約束
。NSLayoutAttributeNotAnAttribute這個(gè)額外解釋一下,當(dāng)我們需要為某個(gè)視圖精確指定一個(gè)寬度或者高度值時(shí),這時(shí)候secondItem為nil,secondAttribute為NSLayoutAttributeNotAnAttribute。relation定義了布局關(guān)系(NSLayoutRelation)。
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
UIView *v1 = [UIView new];
v1.backgroundColor = [UIColor blueColor];
v1.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:v1];
NSLayoutConstraint *topConstraint = [NSLayoutConstraint constraintWithItem:v1
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeTop
multiplier:1
constant:0];
NSLayoutConstraint *leftConstraint = [NSLayoutConstraint constraintWithItem:v1
attribute:NSLayoutAttributeLeft
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeLeft
multiplier:1
constant:0];
NSLayoutConstraint *rightConstraint = [NSLayoutConstraint constraintWithItem:v1
attribute:NSLayoutAttributeRight
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeRight
multiplier:1
constant:0];
NSLayoutConstraint *heightConstraint = [NSLayoutConstraint constraintWithItem:v1
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeHeight
multiplier:0.5
constant:0];
// iOS8 以下
// [v1 addConstraint:topConstraint];
// [v1 addConstraint:leftConstraint];
// [v1 addConstraint:rightConstraint];
// [v1 addConstraint:heightConstraint];
// iOS8及以上
topConstraint.active = YES;
leftConstraint.active = YES;
rightConstraint.active = YES;
heightConstraint.active = YES;
}
2.2 VFL
2.3 自身內(nèi)容尺寸約束、修改約束、布局動(dòng)畫
自身內(nèi)容尺寸約束:一般來說,要確定一個(gè)視圖的精確位置,至少需要4個(gè)布局約束(以確定水平位置x、垂直位置y、寬度w和高度h)。但是,某些用來展現(xiàn)內(nèi)容的用戶控件,例如文本控件UILabel、按鈕UIButton、圖片視圖UIImageView等,它們具有自身內(nèi)容尺寸(Intrinsic Content Size),此類用戶控件會(huì)根據(jù)自身內(nèi)容尺寸添加布局約束。也就是說,如果開發(fā)者沒有顯式給出其寬度或者高度約束,則其自動(dòng)添加的自身內(nèi)容約束將會(huì)起作用。因此看似“缺失”約束,實(shí)際上并非如此。
對(duì)于約束的如下幾個(gè)重要屬性:
@property (readonly, assign) id firstItem;
@property (readonly) NSLayoutAttribute firstAttribute;
@property (readonly) NSLayoutRelation relation;
@property (readonly, assign) id secondItem;
@property (readonly) NSLayoutAttribute secondAttribute;
@property (readonly) CGFloat multiplier;
/* Unlike the other properties, the constant may be modified after constraint creation. Setting the constant on an existing constraint performs much better than removing the constraint and adding a new one that's just like the old but for having a new constant.
*/
@property CGFloat constant;
更新約束:
當(dāng)使用代碼來修改約束時(shí),只能修改約束的常量值constant。一旦創(chuàng)建了約束,其他只讀屬性都是無法修改的,特別要注意的是比例系數(shù)multiplier也是只讀的。
添加刪除約束:
- (void)keyboardWillShow:(NSNotification *)notification
{
self.labelCenterYNormalCons.active = NO;
self.labelCenterYKeyboardCons.active = YES;
}
- (void)keyboardWillHide:(NSNotification *)notification
{
self.labelCenterYKeyboardCons.active = NO;
self.labelCenterYNormalCons.active = YES;
}
盡量先設(shè)置需要將active置為NO的約束,然后再設(shè)置需要將active置為YES的約束,如果顛倒上面兩條語句的話,可能會(huì)引起運(yùn)行時(shí)約束錯(cuò)誤。
修改約束優(yōu)先級(jí):
- (void)keyboardWillShow:(NSNotification *)notification
{
self.labelCenterYNormalCons.priority = UILayoutPriorityDefaultLow;
self.labelCenterYKeyboardCons.priority = UILayoutPriorityDefaultHigh;
}
- (void)keyboardWillHide:(NSNotification *)notification
{
self.labelCenterYKeyboardCons.priority = UILayoutPriorityDefaultLow;
self.labelCenterYNormalCons.priority = UILayoutPriorityDefaultHigh;
}
需要注意的是,只能修改可選約束的優(yōu)先級(jí),也就是說:
- 不允許將優(yōu)先級(jí)由小于1000的值改為1000
- 不允許將優(yōu)先級(jí)由1000修改為小于1000的值
例如,如果將優(yōu)先級(jí)由250修改為1000,則會(huì)拋出異常。
自身內(nèi)容尺寸約束的抗擠壓與抗拉抻效果
彈簧會(huì)有自身固有長(zhǎng)度,當(dāng)有外力作用時(shí),彈簧會(huì)抵抗外力作用,盡量接近固有長(zhǎng)度。
抗拉抻:當(dāng)外力拉長(zhǎng)彈簧時(shí),彈簧長(zhǎng)度大于固有長(zhǎng)度,且產(chǎn)生向內(nèi)收的力阻止外力拉抻,且盡量維持長(zhǎng)度接近自身固有長(zhǎng)度。
抗擠壓:當(dāng)外力擠壓彈簧時(shí),彈簧長(zhǎng)度小于固有長(zhǎng)度,且產(chǎn)生向外頂?shù)牧ψ柚雇饬D壓,且盡量維持長(zhǎng)度接近自身固有長(zhǎng)度。
對(duì)于自身內(nèi)容尺寸約束,Hug值表示抗拉抻優(yōu)先級(jí),CompressionResistance值表示抗壓縮優(yōu)先級(jí)。Hug值越高越難被拉抻,CompressionResistance值越高越難被壓縮。這兩個(gè)都是針對(duì)自身內(nèi)容尺寸。這兩個(gè)值越高,在自動(dòng)布局的時(shí)候,view的真實(shí)布局就越接近自身內(nèi)容尺寸。他們表達(dá)的是一種自身內(nèi)容尺寸約束對(duì)外加約束的抵抗力。