[iOS开发]autolayout自动布局
使用Objective-C風(fēng)格的方法進行代碼autolayout布局
在iOS 6之后系統(tǒng)引入了相關(guān)的類來進行autolayout的代碼方式創(chuàng)建與布局設(shè)置。
使用代碼進行 autolayout 布局首先要了解一個重要的類:NSLayoutConraint。NSLayoutConraint 類是進行代碼autolayout布局的核心類,其創(chuàng)建出具體的自動布局約束對象。使用Xcode 創(chuàng)建一個工程,在ViewController.m文件的viewDidLoad方法中添加如下代碼:
效果:
上面代碼看起來非常復(fù)雜,然而用到的核心方法只有一個,即創(chuàng)建autolayout約束對象:
它是一個類方法:
+ (instancetype)constraintWithItem:(id)view1 attribute:(NSLayoutAttribute)attr1 relatedBy:(NSLayoutRelation)relation toItem:(nullable id)view2 attribute:(NSLayoutAttribute)attr2 multiplier:(CGFloat)multiplier constant:(CGFloat)cconstraintWithItem:attribute:relatedBy:toItem:attribute:multiplier:constant:方法參數(shù)有7個。第1個參數(shù)設(shè)置要約束的第一個視圖對象。第2個參數(shù)設(shè)置約束的第一個參數(shù)的約束屬性,具體 參數(shù)意義后面會介紹。第3個參數(shù)設(shè)置約束屬性間的關(guān)系,參數(shù)具體意義后面會介紹。第4個參數(shù)設(shè)置要約束的第二個視圖對象。第5個參參數(shù)設(shè)置第二個視圖對象的約束屬性。第6個參數(shù)設(shè)置約束的比例。第7個參數(shù)設(shè)置約束的值。
其中,第2個參數(shù)和第5個參數(shù)都需要設(shè)文置為NSLayoutAttribute類型的枚舉,枚舉值為要約束控件的具體屬性,常用枚舉值及含義如下:
邊距和上面那些還是有一定差距的,我們看下效果:
NSLayoutConstraint *constraintX = [NSLayoutConstraint constraintWithItem:myView attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeLeftMargin multiplier:1 constant:0];先看約束NSLayoutAttributeLeftMargin:
再看NSLayoutAttributeLeft:
可以看到Margin是有邊距的,這個后面再研究。上面的枚舉值中,在從左向右的布局結(jié)構(gòu)中,NSLayoutAttributeLeft、NSLayoutAttributeRight和NSLayoutAttributeLeading、NSLayoutAttributeTrailing效果一致。上面所有枚舉值中,有一個比較特殊,即NSLayoutAttributeNotAnAttribute值,它沒有任何意義,只是作為某些情況下方法中的占位,例如:
NSLayoutConstraint *constraintH = [NSLayoutConstraint constraintwithItem:myView attribute:NSLayoutAttributeHeight relatedby:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:100];創(chuàng)建約束對象的方法中,第參數(shù)需要設(shè)置為LouRelation類型的枚舉,這個值決定了所約束屬性間的關(guān)系,枚舉值及含義如下:
typedef NS_ENUM(NSInteger, NSLayoutRelation) {NSLayoutRelationLessThanOrEqual = -1, //小于等于所約束的值NSLayoutRelationEqual = 0, //嚴(yán)格等于所約束的值NSLayoutRelationGreaterThanOrEqual = 1, //大于等于所約束的值 };創(chuàng)建約束對象方法的最后兩個參數(shù)決定了約束值,第6個參數(shù)設(shè)置約束的比例,第7個參數(shù)設(shè)置具體的約束值。例如,需要設(shè)置第1個控件的寬度是第2個控件寬度的2倍多100單位距離,可使用如下代碼:
NSLayoutConstraint *constraintW = [NSLayoutConstraint constraintWithItem:myView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:2 constant:100];multiplier與constant參數(shù)的計算方法遵守如下公式:
view1寬度 = view2寬度 * multiplier + constant運行工程,可以看到,無論橫屏模式還是豎屏模式,也無論屏幕尺寸如何,色塊控件始終出現(xiàn)在屏幕的正中央,寬度、高度均為 100 單位。
小提示:
使用格式化的字符進行autolayout布局對象創(chuàng)建
前面介紹使用代碼進行autolayout布局時,有一個致命的缺陷,一個十分簡單的布局結(jié)構(gòu)卻要寫十分冗長的代碼。此外,還有一種十分神奇的創(chuàng)建autolayout的方法:
- (void)viewDidLoad {[super viewDidLoad];self.view.backgroundColor = [UIColor lightGrayColor];// Do any additional setup after loading the view.UIView * myView = [[UIView alloc]init];myView.translatesAutoresizingMaskIntoConstraints = NO;myView.backgroundColor = [UIColor redColor];//添加約束前,必須將子視圖添加在父視圖上[self.view addSubview:myView];NSArray *constraintArray = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|-20-[myView(100@1000)]" options:0 metrics:nil views:_NSDictionaryOfVariableBindings(@"myView", myView)];NSArray *constraintArray2 = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|-100-[myView(100)]" options:0 metrics:nil views:_NSDictionaryOfVariableBindings(@"myView", myView)];[self.view addConstraints:constraintArray];[self.view addConstraints:constraintArray2]; }代碼一下子簡潔不少,看一下運行效果:
上面代碼中,使用constraintsWithVisualFormat:options:metrics:views:方法用于根據(jù)VFL格式化字符串創(chuàng)建一系列的NSLayoutConstrsint約束對象,這些創(chuàng)建出來的約束對象會以數(shù)組的形式返回。要理解這個方法,首先需要理解什么是VFL。
乍一看,VFL確實十分令人費解,但是完全理解它之后就能感受到它的優(yōu)美之處,它像極了中國古老的象形語言,通過半畫半文字的方式表達信息。上面的第一條語句實際是約束了 myView視圖左側(cè)距離父視圖 20個單位,寬度為100個單位,并且這條約束的優(yōu)先級為1000。第二條語句約束了myView 視圖上側(cè)距離父視圖100個單位,myView視圖的高度為100。現(xiàn)在可以解釋一下 VFL 語言的語法含義了,最前面的H或者V代表約束的布局方向,H(horizontal)是為水平方向添加約束, V(vertical)是為豎直方向添加約束?!皘”表示父視圖的邊緣。在H約束布局中,如果“”出現(xiàn)在字符串的左端,則代表父視圖的左邊界;如果“|”出現(xiàn)在字符串的右端,則代表父視圖的右邊界。在 V約束布局中,如果“|”出現(xiàn)在字符串的左邊,則代表父視圖的上邊界;如果“|”出現(xiàn)在字符串的右端,則代表父視圖的下邊界。“-x-”表示具體的約束距離,x既可為常量也可為變量,例如前面示例中的20就是常量,為變量的情況后面會有討論。[]內(nèi)為要布局?jǐn)[放的控件名稱,()內(nèi)約束控件的尺寸。在H約束布局中,其含義是約束控件的寬度。在V約束布局中,其含義是約束控件的高度。@符號后面的值為設(shè)置此約束的優(yōu)先級。
理解了VFL語句的含義后,再來看通過VFL語句創(chuàng)建約束集合的方法:
constraintsWithVisualFormat:options:metrics:views:方法中第1個參數(shù)為創(chuàng)建約束的VFL字符串,第2個參數(shù)設(shè)置所約束控件的對齊模式,其需要設(shè)置為NSLayoutFormatOptions類型的枚舉。這個枚舉中常用枚舉值及含義如下:
typedef NS_OPTIONS(NSUInteger, NSLayoutFormatOptions) {//約束的控件左對齊NSLayoutFormatAlignAllLeft = (1 << NSLayoutAttributeLeft),//約束的控件右對齊NSLayoutFormatAlignAllRight = (1 << NSLayoutAttributeRight),//約束的控件上對齊NSLayoutFormatAlignAllTop = (1 << NSLayoutAttributeTop),//約束的控件下對齊NSLayoutFormatAlignAllBottom = (1 << NSLayoutAttributeBottom),//約束的控件前對齊NSLayoutFormatAlignAllLeading = (1 << NSLayoutAttributeLeading),//約束的控件后對齊NSLayoutFormatAlignAllTrailing = (1 << NSLayoutAttributeTrailing),//約束的控件X軸中心對齊NSLayoutFormatAlignAllCenterX = (1 << NSLayoutAttributeCenterX),//約束的控件Y軸中心對齊NSLayoutFormatAlignAllCenterY = (1 << NSLayoutAttributeCenterY),NSLayoutFormatAlignAllLastBaseline = (1 << NSLayoutAttributeLastBaseline),NSLayoutFormatAlignAllFirstBaseline API_AVAILABLE(macos(10.11), ios(8.0)) = (1 << NSLayoutAttributeFirstBaseline), #if TARGET_OS_IPHONE//約束的控件文字基線對齊NSLayoutFormatAlignAllBaseline NS_SWIFT_UNAVAILABLE("Use 'alignAllLastBaseline' instead") = NSLayoutFormatAlignAllLastBaseline, #elseNSLayoutFormatAlignAllBaseline = NSLayoutFormatAlignAllLastBaseline, #endifNSLayoutFormatAlignmentMask = 0xFFFF,/* choose only one of these three*/NSLayoutFormatDirectionLeadingToTrailing = 0 << 16, // defaultNSLayoutFormatDirectionLeftToRight = 1 << 16,NSLayoutFormatDirectionRightToLeft = 2 << 16,NSLayoutFormatDirectionMask = 0x3 << 16,#if TARGET_OS_IPHONE/* choose only one spacing format*///僅選擇一種間距格式NSLayoutFormatSpacingEdgeToEdge API_AVAILABLE(ios(11.0),tvos(11.0)) = 0 << 19, // default/* Valid only for vertical layouts. Between views with text content the valuewill be used to determine the distance from the last baseline of the view aboveto the first baseline of the view below. For views without text content the topor bottom edge will be used in lieu of the baseline position.The default spacing "]-[" will be determined from the line heights of the fontsinvolved in views with text content, when present.*//*僅對垂直布局有效。在具有文本內(nèi)容的視圖之間該值將用于確定與上面視圖的最后一個基線的距離到下面視圖的第一個基線。對于沒有文本內(nèi)容的視圖,頂部或下邊緣將用于代替基線位置。默認(rèn)間距“]-[”將從字體的行高確定涉及具有文本內(nèi)容的視圖(如果存在)。*/NSLayoutFormatSpacingBaselineToBaseline API_AVAILABLE(ios(11.0),tvos(11.0)) = 1 << 19,NSLayoutFormatSpacingMask API_AVAILABLE(ios(11.0),tvos(11.0)) = 0x1 << 19, #endif };上面創(chuàng)建約束的方法中第4個參數(shù)為變量映射字典,如果 VFL 字符串中需要使用到某些變量,則需要使用這個參數(shù)將變量映射到VFL字符串中,示例如下:
NSNumber *width = @100; NSNumber *left = @20; NSArray *constraintArray = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|-left-[myView(wid@1000)]" options:0 metrics:@{@"left" : left, @"wid" : width} views:_NSDictionaryOfVariableBindings(@"myView", myView)];上面示例代碼中創(chuàng)建了兩個NSNumber類型的變量量,在 metrics 參數(shù)中,使用字典鍵值對的模式將 VFL 中對應(yīng)的字符串應(yīng)設(shè)置變量對象。
關(guān)于constraintsWithVisualFormat:options:metrics:views:方法中還有最后一個參數(shù)views,它對應(yīng)的參數(shù)是一個約束對象映射字典。與變量映射的原理一樣,需需要將VFL 中使用到的具體控件名映射成視圖控件對象。_NSDictionaryOfVariableBindings()宏可以幫助開發(fā)者快捷地創(chuàng)建這個映射字典,開發(fā)者只需將使用到的視圖控件對象直接傳入。即可。需要注意的是,使用 _NSDictionaryOfVariableBindings()宏進行快捷映射字典創(chuàng)建時,視圖控件的名稱必須和VFL中的名稱完全一致。如果手寫這個映射字典,則上面的代碼和下面是等價的:
管理約束的幾個方法
除了已經(jīng)介紹的添加約束的方法外,autolayout 框架中還提供了一些移除約束的方法。對于進行autolayout約束的視圖控件而言,其中所有可用的有關(guān)設(shè)置約束的方法如下:
//添加一個約束對象 - (void)addConstraint:(NSLayoutConstraint *)constraint API_AVAILABLE(ios(6.0)); // This method will be deprecated in a future release and should be avoided. Instead, set NSLayoutConstraint's active property to YES. //添加一組約束對象 - (void)addConstraints:(NSArray<__kindof NSLayoutConstraint *> *)constraints API_AVAILABLE(ios(6.0)); // This method will be deprecated in a future release and should be avoided. Instead use +[NSLayoutConstraint activateConstraints:]. //移除一個約束對象 - (void)removeConstraint:(NSLayoutConstraint *)constraint API_AVAILABLE(ios(6.0)); // This method will be deprecated in a future release and should be avoided. Instead set NSLayoutConstraint's active property to NO. //移除一組約束對象 - (void)removeConstraints:(NSArray<__kindof NSLayoutConstraint *> *)constraints API_AVAILABLE(ios(6.0));應(yīng)用
@interface ViewController ()<UITextViewDelegate>@property (nonatomic, strong) UITextView *textView; @property (nonatomic, strong) NSArray *array1; @property (nonatomic, strong) NSArray *array2;@end@implementation ViewController- (void)viewDidLoad {[super viewDidLoad];_textView = [[UITextView alloc] init];_textView.layer.borderColor = [UIColor grayColor].CGColor;_textView.layer.borderWidth = 1;_textView.translatesAutoresizingMaskIntoConstraints = NO;_textView.delegate = self;[self.view addSubview:_textView];_array1 = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|-100-[textView]-100-|" options:0 metrics:nil views:_NSDictionaryOfVariableBindings(@"textView", _textView)];_array2 = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|-150-[textView(30)]" options:0 metrics:nil views:_NSDictionaryOfVariableBindings(@"textView", _textView)];[self.view addConstraints:_array1];[self.view addConstraints:_array2]; }- (CGSize)getContentSize:(UITextView*)myTextView{return [myTextView sizeThatFits:CGSizeMake(myTextView.frame.size.width, FLT_MAX)]; }- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {if (_textView.contentSize.height != _textView.frame.size.height && _textView.contentSize.height < 100) {float height = textView.contentSize.height;[self.view removeConstraints:_array2];_array2 = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|-150-[textView(height)]" options:0 metrics:@{@"height" : [NSNumber numberWithFloat:height]} views:_NSDictionaryOfVariableBindings(@"textView", _textView)];[self.view addConstraints:_array2];}[self.view updateConstraintsIfNeeded];return YES; }@end實現(xiàn)一個可以自適應(yīng)多行輸入高度的UITextView。
拋出疑問: 不知道為什么輸入字符時第二行只有一個字符時UITextView的contentSize不改變,或者刪除字符時第二行刪光后UITextView的contentSize也不改變。
總結(jié)
以上是生活随笔為你收集整理的[iOS开发]autolayout自动布局的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 部署好网站,同局域网中电脑无法访问的问题
- 下一篇: 第5章-css选择器初级和背景