AutoLayout简介
Autolayout是一种全新的布局技术,专门用来布局UI界面的,用来取代Frame布局在遇见屏幕尺寸多重多样的问题。Autolayout自iOS 6开始引入,但是由于Xcode 4的不给力,当时并没有得到大规模推广。在iOS 7(Xcode5)开始,Autolayout的开发效率得到很大的提升,苹果官方也推荐开发者尽量使用Autolayout来布局UI界面,减少纯代码的方式。
那么AutoLayout怎么使用呢?
VFL
VFL(Virsual Format Language)是一种虚拟的格式化语言,主要用来创建AutoLayout的约束字符串。
示例,如:V: |-(0)-Label1-(0)-Label2-(0)-| 方向:从左到右,从上到下
V:表示方向为垂直方向,也就是竖向;H为横向。
|:竖线表示为边界(当前所在View的边界),这里紧邻方向表示符V,方向是从上到下,因此表示上面界。
0:NSNumber 0 表示约束值为0。这里是Label1距离上边界的约束为0。
Label1:表示对象Label1。
0:表示Label1和Label2的约束为0.
Label2:表示对象Label2。
0:表示Label2和下边界的约束为0.
|:表示下边界。
关于[VFL官网]详细知识,请查看官方的介绍
AutoLayout IB使用方式
为了让布局能够在不同屏幕的size上都能够表现正常,我们需要对其增加“约束”。然后,在不同屏幕尺寸下view就能够按照约束来局。
添加如下约束:
属性说明:
1:距离边缘 最上面的4个虚线表示某个View的距离上边 左边 右边 下边多高
2:那个蓝色的Constrain To Margins 是iPhone6出现之后。
Apple 觉得更大的分辨率有点间距好看, 默认为8 , 如果这个勾上了 这个View距离四周的值就变成了 你输入的值+8。 一般建议勾掉 。
案例1
设置某个View距离父View上下左右间距全部为20。
案例2
某个View距离父View的左侧20,上20,宽高均为100。
注意:我在添加约束的时候有个选项叫做updateFrame 如果勾选 会直接将Frame调整到真实值 ,而不需要再次update 。
案例3
某个View距离在父View的左侧20 案例2中白色View 上20 宽高和Demo2中的宽高一样。
然后,点击某个约束。
对其处理
属性说明:
- Leading Edges:左对齐
- Trailing Edges:右对齐
- Top Edges:上对齐
- Bottom Edges:下对齐
- Horizontal Centers:水平中心对齐
- Vertical Centers:竖向中心对齐
- Baselines:基线对齐
- Horizontal Center in Container:对齐容器中的水平中心
- Vertical Center in Container:对齐容器中的竖向中心
案例4
某个View距离在父View的右侧20 案例3中白色View上20 宽高和案例3中的宽高一样 并且对齐。
案例5
某个label和另外一个label基线对齐。
像label 默认是有宽度的 宽度就是字体自适应的。这样我们就可以不给UIlabel 高度 把Label的NumberOfline = 0就可以自适应高度了。
Tip
1,有时候约束太多的时候 我们可以给某个View起个假名字以起到唯一标识的作用。
2,View总是选不中怎么办?按 ctrl + shift + 单击。
3,当ScrollView过长无法编辑怎么办?将控制器改为Freedom 修改ContentView的高度约束 这样ScrollView 就可以滚动了。
UItableViewCell高度计算
为了方便说明,本部分知识主要从以下几个方面讲解。
- AutoLayout with UILabel in UITableViewCell
- AutoLayout with UITextView in UITableViewCell
- Manual Layout with UILabel in UITableViewCell
- Manual Layout with UITextView in UITableViewCell
- 随UITextView高度动态改变Cell高度
AutoLayout with UILabel
创建一个空的xib,命名为C1.xib, 然后拖入一个UITableViewCell控件。接着创建一个UITableViewCell的子类,命名为C1类。然后在C1.xib中,将与C1类进行关联。只需要在Class那里写入关联的类名C1即可。
还有由于UITableViewCell需要重用功能,所以我们还需要设置一个重用标识。
下面是使用autoLayout的布局。
接着我们在UITableView中来使用我们自定义的UITableViewCell C1。首先我们创建一个UITableViewController的子类T1ViewController, 接着在Main.storyboard中拖入一个UITableViewController,并关联T1ViewController。
Auto Layout with UITextView
同样参考上面我们创建一个C2.xib, UITableViewCell的子类C2,并关联C2.xib与C2类。并在C2.xib中对其布局,同样使用了auto layout. 布局如下图:
创始UITableViewController的了类T2ViewController,在Main.storyboard中拖入UITableViewController,并关联他们。接着代码中注册C2.xib到UITableView。
如下面是计算UITableView高度的代码:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
C2 *cell = (C2 *)self.prototypeCell;
cell.t.text = [self.tableData objectAtIndex:indexPath.row];
CGSize size = [cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize];
CGSize textViewSize = [cell.t sizeThatFits:CGSizeMake(cell.t.frame.size.width, FLT_MAX)];
CGFloat h = size.height + textViewSize.height;
h = h > 89 ? h : 89; //89是图片显示的最低高度, 见xib
NSLog(@"h=%f", h);
return 1 + h;
}
在这儿我们是通过sizeThatFits:计算的UITextView的高度,然后加上systemLayoutSizeFittingSize:返回的高度。为什么要这样呢? 因为UITextView内容的高度不会影响systemLayoutSizeFittingSize计算。
下面是UITextView的实例:
此图中距顶的约束是10, 距底的约束8, 距左边约束是87,距右边的约束是13, 那么systemLayoutSizeFittingSize:返回的CGSize为height等于19, size等于100. 它UITextView的frame是不影响systemLayoutSizeFittingSize:的计算。所以,我们需要加上textViewSize.height。
Manual Layout with UILabel
按照前面介绍的,我们需要创建C3.xib, C3类, T3ViewController类,Main.storyboard中拖入UITableViewController,并分别建立关联。 为了简单,C3.xib中我就不加padding之类的了,如图:
然后添加如下的计算代码:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
C3 *cell = [self.tableView dequeueReusableCellWithIdentifier:@"C3"];
cell.t.text = [self.tableData objectAtIndex:indexPath.row];
[cell.t sizeToFit];
return cell;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
C3 *cell = (C3 *)self.prototypeCell;
NSString *str = [self.tableData objectAtIndex:indexPath.row];
cell.t.text = str;
CGSize s = [str calculateSize:CGSizeMake(cell.t.frame.size.width, FLT_MAX) font:cell.t.font];
CGFloat defaultHeight = cell.contentView.frame.size.height;
CGFloat height = s.height > defaultHeight ? s.height : defaultHeight;
NSLog(@"h=%f", height);
return 1 + height;
}
这里,用到了一个NSString的Cagetory方法,代码如下:
- (CGSize)calculateSize:(CGSize)size font:(UIFont *)font
{
CGSize expectedLabelSize = CGSizeZero;
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7) {
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
paragraphStyle.lineBreakMode = NSLineBreakByWordWrapping;
NSDictionary *attributes = @{NSFontAttributeName:font, NSParagraphStyleAttributeName:paragraphStyle.copy};
expectedLabelSize = [self boundingRectWithSize:size options:NSStringDrawingUsesLineFragmentOrigin attributes:attributes context:nil].size;
}
else {
expectedLabelSize = [self sizeWithFont:font
constrainedToSize:size
lineBreakMode:NSLineBreakByWordWrapping];
}
return CGSizeMake(ceil(expectedLabelSize.width), ceil(expectedLabelSize.height));
}
运行效果如下:
Manual Layout with UITextView
按照前面介绍的,我们需要创建C4.xib, C4类, T4ViewController类,Main.storyboard中拖入UITableViewController,并分别建立关联。 为了简单,C4.xib中我就不加padding之类的了,如图:
相关代码如下:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
C4 *cell = [self.tableView dequeueReusableCellWithIdentifier:@"C4"];
cell.t.text = [self.tableData objectAtIndex:indexPath.row];
[cell.t sizeToFit];
return cell;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
C4 *cell = (C4 *)self.prototypeCell;
NSString *str = [self.tableData objectAtIndex:indexPath.row];
cell.t.text = str;
CGSize s = [cell.t sizeThatFits:CGSizeMake(cell.t.frame.size.width, FLT_MAX)];
CGFloat defaultHeight = cell.contentView.frame.size.height;
CGFloat height = s.height > defaultHeight ? s.height : defaultHeight;
return 1 + height;
}
随UITextView高度改变Cell高度
当UITextView内容改变的时候,计算自身高度,然后通知UITableView更新,这样就会触发UITableViewCell高度重新计算,从而改变Cell的高度。
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
C5 *cell = [self.tableView dequeueReusableCellWithIdentifier:@"C5"];
cell.t.text = @"123";
cell.t.delegate = self;
return cell;
}
#pragma mark - UITableViewDelegate
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
C5 *cell = (C5 *)self.prototypeCell;
cell.t.text = self.updatedStr;
CGSize s = [cell.t sizeThatFits:CGSizeMake(cell.t.frame.size.width, FLT_MAX)];
CGFloat defaultHeight = cell.contentView.frame.size.height;
CGFloat height = s.height > defaultHeight ? s.height : defaultHeight;
return 1 + height;
}
#pragma mark - UITextViewDelegate
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {
if ([text isEqualToString:@"\n"]) {
NSLog(@"h=%f", textView.contentSize.height);
}
return YES;
}
- (void)textViewDidChange:(UITextView *)textView {
self.updatedStr = textView.text;
[self.tableView beginUpdates];
[self.tableView endUpdates];
}
关于UITableViewCell使用自动布局的优化可以查看下面的介绍:
优化UITableViewCell高度计算。
StackView
UIStackView是iOS9新引入的控件,它支持垂直和水平排列多个子视图(SubView)。例如:水平放置三个按钮,等宽,并且按钮间的间隙为10,如果自己实现会比较麻烦,而使用UIStackView则很容易实现。UIStackView目前只支持iOS9+版本,如果要在iOS 7版本上使用UIStackView,可以使用下面两个第三方库:OAStackView和TZStackView。其中:
- OAStackView,基于OC的StackView库,支持iOS7+以上的系统,同时支持代码和IB视图。
- TZStackView,基于Swift的StackView库,同样支持iOS7+以上的系统,但是不支持storyboard。
OAStackView实现子视图等分
案例1
例如,下面的例子是使用OAStackView实现视图等分的例子。
相关代码如下:
UILabel *l1 = [[UILabel alloc] init];
l1.text = @"Label 1";
UILabel *l2 = [[UILabel alloc] init];
l2.text = @"Label 2";
OAStackView *stackView = [[OAStackView alloc] initWithArrangedSubviews:@[l1, l2]];
stackView.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:stackView];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-30-[stackView]"
options:0
metrics:0
views:NSDictionaryOfVariableBindings(stackView)]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-10-[stackView]"
options:0
metrics:0
views:NSDictionaryOfVariableBindings(stackView)]];
上面的例子实现了两个按钮的垂直排列。如果想要水平排列,修改stackView.axis值为UILayoutConstraintAxisHorizontal即可。需要注意,因为不是用IB创建的View,所以要设定View的translatesAutoresizingMaskIntoConstraints属性为NO,否则排列属性不生效。当非IB创建时,属性默认为YES;当IB创建View时,属性默认为NO。
案例2
在水平方向上放4张图片,图片等分。
1,首先在页面上拖拽1个imageView,将它的宽高都设置成50。
2,然后再添加三个imageView,将界面上的四个ImageView设置等宽等高。
3,然后再将他们加入到Stack View中,设置Stack View 的distribution属性为 Equal Spacing(等间距)。
4,最后,设置Stack View 的位置