关于界面布局,apple的策略已经趋于成熟,autolayout的优点在开发中也已经展示的淋漓尽致。除了使用storyBoard进行布局约束的拖拽,有时咱们也须要在代码中进行autolayout的布局设置,Masonry库能够方便的建立约束属性,实际上,咱们也没有必要再使用系统原生的代码来建立和设置约束,这篇博客只做为使用的方法备忘。前几篇布局介绍的连接以下:app
使用autoresizing进行界面布局:http://my.oschina.net/u/2340880/blog/423357框架
初识autolayout布局模型:http://my.oschina.net/u/2340880/blog/423500工具
用代码来作视图间的相关约束,那么就必定要将“约束”也进行对象化,在iOS6以后,引入了autolayout这个概念,相应的也增长了NSLayoutConstraint这个对象,这个对象就是专门用来进行约束布局的设置对象。经过这个对象,咱们能够设置相似视图对象之间的间距,约束的宽高,比例等属性。建立NSLayoutConstraint对象的方法有两种,下面咱们分别介绍:布局
所谓Objective-C风格的方法,就是经过原生枚举和一些属性设置来建立NSLayoutConstraint对象。使用NSLayoutConstraint类的以下方法:动画
+(instancetype)constraintWithItem:(id)view1 attribute:(NSLayoutAttribute)attr1 relatedBy:(NSLayoutRelation)relation toItem:(nullable id)view2 attribute:(NSLayoutAttribute)attr2 multiplier:(CGFloat)multiplier constant:(CGFloat)c;
先来介绍下方法中的参数都是什么意义,咱们应该怎么用他们:spa
view1:要添加约束的视图对象。.net
attr1:要约束的对象属性,这个就是一些枚举,以下:设计
typedef NS_ENUM(NSInteger, NSLayoutAttribute) { NSLayoutAttributeLeft = 1,//左 NSLayoutAttributeRight,//右 NSLayoutAttributeTop,//上 NSLayoutAttributeBottom,//下 NSLayoutAttributeLeading,//起始边,相似左,只在某些从右向左排列的语言中和NSLayoutAttributeLeft有大区别 NSLayoutAttributeTrailing,//结束边 NSLayoutAttributeWidth,//宽度 NSLayoutAttributeHeight,//高度 NSLayoutAttributeCenterX,//x中心 NSLayoutAttributeCenterY,//y中心 NSLayoutAttributeBaseline,//基线 NSLayoutAttributeLastBaseline = NSLayoutAttributeBaseline, NSLayoutAttributeFirstBaseline NS_ENUM_AVAILABLE_IOS(8_0), //下面的属性是设置的边距 意义和上面相似 对应左,右等边距 NSLayoutAttributeLeftMargin NS_ENUM_AVAILABLE_IOS(8_0), NSLayoutAttributeRightMargin NS_ENUM_AVAILABLE_IOS(8_0), NSLayoutAttributeTopMargin NS_ENUM_AVAILABLE_IOS(8_0), NSLayoutAttributeBottomMargin NS_ENUM_AVAILABLE_IOS(8_0), NSLayoutAttributeLeadingMargin NS_ENUM_AVAILABLE_IOS(8_0), NSLayoutAttributeTrailingMargin NS_ENUM_AVAILABLE_IOS(8_0), NSLayoutAttributeCenterXWithinMargins NS_ENUM_AVAILABLE_IOS(8_0), NSLayoutAttributeCenterYWithinMargins NS_ENUM_AVAILABLE_IOS(8_0), //无,后面会说应用场景 NSLayoutAttributeNotAnAttribute = 0 };
relation:约束的选项,对应<=,==,>=这些,枚举以下:code
typedef NS_ENUM(NSInteger, NSLayoutRelation) { NSLayoutRelationLessThanOrEqual = -1,//<= NSLayoutRelationEqual = 0,//== NSLayoutRelationGreaterThanOrEqual = 1,//>= };
view2:与之对应添加约束的视图对象,例如,如过我要设置view1的上边距离父视图的上边必定间距,这个view2就是view1的父视图,若是我要设置view1与另外一个视图必定距离,这个view2就是另外一个视图。orm
attr2:view2的要约束的属性,和attr1含义同样。
multiplie:约束的比例,好比view1的宽是view2的宽的两倍,这个multiplie就是2.
C:这是具体的约束值
对于这些属性,文档上有这样的解释:view1.attr1 = view2.attr2 * multiplier + constant
例如,咱们建立一个label,将它的宽高固定为100*100,位置放在屏幕的中央,咱们可使用以下的约束代码:
UILabel * label = [[UILabel alloc]init]; label.numberOfLines = 0; //使用代码布局 须要将这个属性设置为NO label.translatesAutoresizingMaskIntoConstraints = NO; label.backgroundColor = [UIColor redColor]; //建立x居中的约束 NSLayoutConstraint * constraintx = [NSLayoutConstraint constraintWithItem:label attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterX multiplier:1 constant:0]; //建立y居中的约束 NSLayoutConstraint * constrainty = [NSLayoutConstraint constraintWithItem:label attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterY multiplier:1 constant:0]; //建立宽度约束 NSLayoutConstraint * constraintw = [NSLayoutConstraint constraintWithItem:label attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:100]; //建立高度约束 NSLayoutConstraint * constrainth = [NSLayoutConstraint constraintWithItem:label attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:100]; //添加约束以前,必须将视图加在父视图上 [self.view addSubview:label]; [self.view addConstraints:@[constraintx,constrainty,constrainth,constraintw]];
效果以下:
能够发现,一个如此简单的约束方式,咱们用这样的代码要写这么一大坨,麻烦并且不直观。因而,apple又提供给咱们下面一种方式。
看到这个小标题是否是眼前一亮,这个标题不是我凭空想象出来的,apple的文档上就是这么写的。十分可爱,对吧。相对于NSLayoutConstraint中的建立方法以下:
+ (NSArray<__kindof NSLayoutConstraint *> *)constraintsWithVisualFormat:(NSString *)format options:(NSLayoutFormatOptions)opts metrics:(nullable NSDictionary<NSString *,id> *)metrics views:(NSDictionary<NSString *, id> *)views;
那么咱们先来看,什么是格式化的字符串约束。
说到格式化的字符串约束,要先提一个东西VFL:visual format language——格式化约束语言,这又是什么鬼?确实,这个东西写出来第一眼看上去真的不知道是什么鬼,就好比要设置一个label,距离上边100,左边20,宽高都为100,代码以下:
label.numberOfLines = 0; label.translatesAutoresizingMaskIntoConstraints = NO; label.backgroundColor = [UIColor redColor]; //label.text=@"12332322132131233213213"; [self.view addSubview:label]; NSArray * constraintArray = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|-20-[label(100@1000)]" options:0 metrics:nil views:NSDictionaryOfVariableBindings(label)]; NSArray * constraintArray2 = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|-100-[label(100)]" options:0 metrics:nil views:NSDictionaryOfVariableBindings(label)]; [self.view addConstraints:constraintArray]; [self.view addConstraints:constraintArray2];
效果以下:
代码少了不少,对吧,可是中间那部分字符串什么玩意?下面咱们来解释一下。
VFL语言我我的而言,他很相似于古代的象形文字(不知道是否apple的工程师从其中获得灵感),对布局的约束设置是直观的用符号表达出来的,例如:
H:|-20-[label(100@1000)]
前面的H表明是水平的布局仍是垂直的布局,H表明水平,V表示垂直,|表示父视图的边沿,-20-表示距离20px,[]内是要布局摆放的视图对象名,()中是约束的尺寸,H下则为宽度,V下则为高度,@后面的数字表明优先级。
建立方法中的options参数,用来设置对齐模式,不须要能够写0:
typedef NS_OPTIONS(NSUInteger, NSLayoutFormatOptions) { NSLayoutFormatAlignAllLeft = (1 << NSLayoutAttributeLeft), NSLayoutFormatAlignAllRight = (1 << NSLayoutAttributeRight), NSLayoutFormatAlignAllTop = (1 << NSLayoutAttributeTop), NSLayoutFormatAlignAllBottom = (1 << NSLayoutAttributeBottom), NSLayoutFormatAlignAllLeading = (1 << NSLayoutAttributeLeading), NSLayoutFormatAlignAllTrailing = (1 << NSLayoutAttributeTrailing), NSLayoutFormatAlignAllCenterX = (1 << NSLayoutAttributeCenterX), NSLayoutFormatAlignAllCenterY = (1 << NSLayoutAttributeCenterY), NSLayoutFormatAlignAllBaseline = (1 << NSLayoutAttributeBaseline), NSLayoutFormatAlignAllLastBaseline = NSLayoutFormatAlignAllBaseline, NSLayoutFormatAlignAllFirstBaseline NS_ENUM_AVAILABLE_IOS(8_0) = (1 << NSLayoutAttributeFirstBaseline), NSLayoutFormatAlignmentMask = 0xFFFF, /* choose only one of these three */ NSLayoutFormatDirectionLeadingToTrailing = 0 << 16, // default NSLayoutFormatDirectionLeftToRight = 1 << 16, NSLayoutFormatDirectionRightToLeft = 2 << 16, NSLayoutFormatDirectionMask = 0x3 << 16, };
metrics参数是属性替换字典,例如咱们上边用到的距离左边界20,若是这个20是一个变量width,咱们能够将20的地方换成width,而后配置这个字典:@{@"width":@20},这样,在布局时,系统会把width换成20。
views是对象的映射字典,原理也是将字符串中的对象名label映射成真实的对象,NSDictionaryOfVariableBindings会帮咱们生成这样的字典,咱们只须要想对象传进去便可,若是要手动建立这字典,格式以下:@{@"label":label}.
仔细观察QQ或者其余聊天工具的app上的输入框,会发现他很是智能,宽度会随着咱们输入文字的行数进行自适应,而且这个宽度不是无限增大的,当咱们文字多到必定行数,宽度会保持不变,文本框能够进行内容滑动,若是不用autolayout,这个功能会比较棘手一些,可是使用它,会发现这是如此的容易:
@interface ViewController ()<UITextViewDelegate> { UITextView * textView ; NSArray * array1; NSArray * array2; } @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. 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)]; array2 = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|-150-[textView(30)]" options:0 metrics:nil views:NSDictionaryOfVariableBindings(textView)]; [self.view addConstraints:array1]; [self.view addConstraints:array2]; } -(BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text{ //当文本高度大于textView的高度而且小于100时,更新约束 if (textView.contentSize.height>textView.frame.size.height&&textView.contentSize.height<100) { float hight =textView.contentSize.height; //将之前的移除掉 [self.view removeConstraints:array1]; [self.view removeConstraints:array2]; array1 = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|-100-[textView]-100-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(textView)]; array2 = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|-150-[textView(hight)]" options:0 metrics:@{@"hight":[NSNumber numberWithFloat:hight]} views:NSDictionaryOfVariableBindings(textView)]; [self.view addConstraints:array1]; [self.view addConstraints:array2]; } //更新约束 [self.view updateConstraintsIfNeeded]; return YES; }
如今,当咱们进行输入的时候,textView的高度能够自适应文字行数了。
这一点很是coll,上面咱们已经实现了textView随文本的行数高度进行自适应,可是变化的效果十分生硬,还要apple的动画框架支持autolayout,把刚才调用更新约束的地方进行以下修改:
[UIView animateWithDuration:1 animations:^{ [self.view layoutIfNeeded]; }];
试试看,变换的效果已经很是平滑了。
专一技术,热爱生活,交流技术,也作朋友。
——珲少 QQ群:203317592