iOS自動(dòng)布局-筆記

iOS自動(dòng)布局

個(gè)人Github博客,求關(guān)注

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ì)外加約束的抵抗力。

參考文檔:

  1. iOS 8 Auto Layout界面自動(dòng)布局系列1-自動(dòng)布局的基本原理
  2. iOS 8 Auto Layout界面自動(dòng)布局系列2-使用Xcode的Interface Builder添加布局約束
  3. iOS 8 Auto Layout界面自動(dòng)布局系列3-使用代碼添加布局約束
  4. iOS 8 Auto Layout界面自動(dòng)布局系列4-使用VFL添加布局約束
  5. iOS 8 Auto Layout界面自動(dòng)布局系列5-自身內(nèi)容尺寸約束、修改約束、布局動(dòng)畫
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容