Auto Layout: Programmatic Constraints

Auto Layout: Programmatic Constraints

Apple recommends that you create and constrain your views in a XIB file whenever possible. However, if your views are created in code, then you will need to constrain them programmatically.

蘋果建議通過XIB文件創(chuàng)建視圖以及為視圖添加約束,然而如果視圖是通過代碼方式創(chuàng)建的,則要通過代碼方式添加約束。

如果要通過代碼方式創(chuàng)建和約束完整的view hierarchy,則重寫loadView方法。如果需要一個(gè)單獨(dú)的view,然后將其添加到通過XIB創(chuàng)建的view hierarchy中,則重寫viewDidLoad方法來創(chuàng)建及添加約束。

本章我們通過代碼方式重新創(chuàng)建XIB中的image view,然后將其添加到XIB生成的view hierarchy,所以重寫viewDidLoad方法:

- (void)viewDidLoad{
    [super viewDidLoad];
    
    UIImageView *iv = [[UIImageView alloc] initWithImage:nil];
    
    iv.contentMode = UIViewContentModeScaleAspectFit;
    
    iv.translatesAutoresizingMaskIntoConstraints = NO;
    
    [self.view addSubview:iv];
    
    self.imageView = iv;
}

autoresizing masks

在引入Auto Layout之前,iOS應(yīng)用使用autoresizing masks去讓view在不同大小屏幕上縮放。
每個(gè)view都有autoresizing mask,默認(rèn)情況下,iOS創(chuàng)建匹配autoresizing mask的約束并添加到view上,這些translated constraints經(jīng)常和明確指定的約束沖突,導(dǎo)致unsatisfiable constraints。
為了規(guī)避這種問題,將translatesAutoresizingMaskIntoConstraints屬性設(shè)置為NO,來關(guān)閉translated constraints。

VFL - Visual Format Language

如果以代碼方式添加約束,蘋果推薦使用VFL。VFL通過字符串來描述約束,一個(gè)VFL字符串可以描述多個(gè)約束,但是一個(gè)VFL不能同時(shí)描述水平和垂直方向的約束。

對(duì)于image view,需要兩個(gè)VFL字符串來分別描述水平和垂直方向的約束。

  • 水平方向的約束:
@"H:|-0-[imageView]-0-|"

H:代表水平方向的約束,[view]來標(biāo)識(shí)視圖,|代表view's container,這個(gè)VFL約束的意思是:image view左側(cè)方向距其容器0 point,右側(cè)方向距其容器0 point。

當(dāng)是0 point時(shí),-0-可以省略,所以上面的VFL等價(jià)與:

@"H:|[imageView]|"
  • 垂直方向的約束
@"V:[dateLabel]-8-[imageView]-8-[toolbar]"

V:代表垂直方向,image view top方向距date label 8 points,bottom方向距toolbar 8 points。
視圖間的標(biāo)準(zhǔn)間距是8 points,-默認(rèn)設(shè)置間距為8 points,所以上面的VFL等價(jià)與:

@"V:[dateLabel]-[imageView]-[toolbar]"

如果有兩個(gè)image view在水平方向排列,間距是10 points,左側(cè)image view距容器左側(cè)20 points,右側(cè)image view距容器右側(cè)20 points,則其VFL為:

@"H:|-20-[imageViewLeft]-10-[imageViewRight]-20-|"

設(shè)置視圖高度為50 points:

@"V:[someView(==50)]"

Creating Constraints

constraint是NSLayoutConstraint的實(shí)例,創(chuàng)建約束調(diào)用類方法:

+ (NSArray *)constraintsWithVisualFormat:(NSString *)format options:(NSLayoutFormatOptions)opts metrics:(NSDictionary *)metrics views:(NSDictionary *)views;

該方法返回一個(gè)NSLayoutConstraint實(shí)例數(shù)組,因?yàn)橐粋€(gè)VFL可以描述多個(gè)約束。

第一個(gè)參數(shù)是VFL,第四個(gè)參數(shù)是字典,其中字義了VFL中使用的視圖名稱對(duì)應(yīng)的視圖對(duì)象。

定義視圖名稱的KEY,可以是任意字符,但是不能是|,這個(gè)是保留字符,代表view's container。

- (void)viewDidLoad{
    [super viewDidLoad];
    
    UIImageView *iv = [[UIImageView alloc] initWithImage:nil];
    
    iv.contentMode = UIViewContentModeScaleAspectFit;
    
    iv.translatesAutoresizingMaskIntoConstraints = NO;
    
    [self.view addSubview:iv];
    
    self.imageView = iv;
    
    // 創(chuàng)建水平和垂直方向約束
    NSDictionary *nameMap = @{@"imageView": self.imageView,
                              @"dateLabel": self.dateLabel,
                              @"toolbar": self.toolbar};
    
    NSArray *horizontalConstraints = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|-0-[imageView]-0-|" options:0 metrics:nil views:nameMap];
    
    NSArray *verticalConstraints = [NSLayoutConstraint constraintsWithVisualFormat:@"V:[dateLabel]-[imageView]-[toolbar]" options:0 metrics:nil views:nameMap];
}

Adding Constraints

現(xiàn)在已經(jīng)創(chuàng)建了兩個(gè)NSLayoutConstraint對(duì)象的數(shù)組,但是還需要調(diào)用UIView實(shí)例的如下方法將約束添加給view:

- (void)addConstraints:(NSArray *)constraints

應(yīng)該哪個(gè)view來調(diào)用這個(gè)方法呢?以下規(guī)則來確定在哪個(gè)view上添加約束:

  1. 如果一個(gè)約束影響兩個(gè)view,而且這兩個(gè)view有相同的superview,則將約束添加他們的superview上(圖中約束A)。
  2. 如果一個(gè)約束只影響一個(gè)view,則將約束添加到此view上(圖中約束B)。
  3. 如果一個(gè)約束影響兩個(gè)view,但是這兩個(gè)view的superview不相同,不過他們有共同的祖先view,則將約束添加到祖先view上(圖中約束C)。
  4. 如果一個(gè)約束影響一個(gè)view以及其superview,則將約束添加到superview(圖中約束D)。

Intrinsic Content Size

http://stackoverflow.com/questions/15850417/cocoa-autolayout-content-hugging-vs-content-compression-resistance-priority

固有內(nèi)容大小,image view的固有內(nèi)容大小是其中的圖片大小。Auto Layout根據(jù)view的intrinsic content size會(huì)創(chuàng)建一些約束,這類約束有兩個(gè)優(yōu)先級(jí):Content hugging priority和Content compression resistance priority。

Priority Description
Content hugging view到其intrinsic content的距離是否可以增大的優(yōu)先級(jí),如果值是1000,表示view不能大于其intrinsic content的大小;當(dāng)值小于1000,view可以大于其內(nèi)容大小。此優(yōu)先級(jí)越小,越可能變大
Content compression resistance 避免view收縮的優(yōu)先級(jí),如果值是1000,表示view不能小于其內(nèi)容大??;當(dāng)值小于1000,view可以自由收縮。此優(yōu)先級(jí)越小,越可能收縮

這兩個(gè)屬性還可具體到水平和垂直方向,在Interface Builder中可查看這些priority。


我們可以看到value text field的Content hugging vertical priority是250,而image view的Content hugging vertical priority是251,text field的更小,所以Auto Layout選擇讓value text field變得更大,就導(dǎo)致了下圖的效果。



要解決此問題,將image view的Content hugging vertical priority改為200,讓Auto Layout選擇讓image view來變得更大。

[self.imageView setContentHuggingPriority:200 forAxis:UILayoutConstraintAxisVertical];

其他方式添加約束

如果約束不能通過VFL創(chuàng)建,比如,不能通過VFL創(chuàng)建比例類約束,例如約束一個(gè)image view的寬度是其高度的1.5倍。此時(shí)可以通過NSLayoutConstraint的另外一個(gè)方法來添加。

+ (id)constraintWithItem:(id)view1
               attribute:(NSLayoutAttribute)attr1 
               relatedBy:(NSLayoutRelation)relation
                  toItem:(id)view2
               attribute:(NSLayoutAttribute)attr2 
              multiplier:(CGFloat)multiplier 
                constant:(CGFloat)c

創(chuàng)建約束:view1.attr1 = view2.attr2 * multiplier + constant,如果沒有view2和attr2,用nil和NSLayoutAttributeNotAnAttribute代替。

attribute可以是以下常量值:

  • NSLayoutAttributeLeft
  • NSLayoutAttributeRight
  • NSLayoutAttributeTop
  • NSLayoutAttributeBottom
  • NSLayoutAttributeWidth
  • NSLayoutAttributeHeight
  • NSLayoutAttributeBaseline
  • NSLayoutAttributeCenterX
  • NSLayoutAttributeCenterY
  • NSLayoutAttributeLeading
  • NSLayoutAttributeTrailing

為image view添加約束,其寬度是高度的1.5倍

NSLayoutConstraint * aspectConstraint = 
        [NSLayoutConstraint constraintWithItem:self.imageView 
                                     attribute:NSLayoutAttributeWidth 
                                        toItem:self.imageView 
                                     attribute:NSLayoutAttributeHeight 
                                    multiplier:1.5 
                                      constant:0.0];

將約束添加到視圖,執(zhí)行view的以下方法,到底是哪個(gè)view執(zhí)行此方法,和之前VFL確定view的規(guī)則一致。

- (void)addConstraint:(NSLayoutConstraint *)constraint

此處該約束只影響image view,所以由image view來調(diào)用添加約束的方法:

[self.imageView addConstraint:aspectConstraint];

本文是對(duì)《iOS Programming The Big Nerd Ranch Guide 4th Edition》第十六章的總結(jié)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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