[IOS]今天开始学UI---Autolayout

AutoLayout可以做什么?

iphone,或者ipad屏幕的分辨率存在这多种款式,单纯的依靠frame布局来解决不同屏幕间的适配会给代码增加复杂性。

你可以拿到window的尺寸,然后经过各种繁琐的计算最后确定下合适的尺寸,但是这样会大大增加代码的复杂性,让布局称为一种额外的开销。AutoLayout很好的解决了这一问题。

AutoLayout是什么?

AutoLayout是一种基于约束的,描述性布局系统

基于约束:和以往定义frame的位置和尺寸不同,AutoLayout的位置确定是以所谓相对位置的约束来定义的,比如x坐标为superView的中心,y坐标为屏幕底部上方10像素等 ,尺寸也是可以通过相对尺寸调整,比如width为superView width的0.5倍。

描述性:约束的定义和各个view的关系使用接近自然语言或者可视化语言的方法来进行描述 。

布局系统:用来负责界面的各个元素的位置 ,AutoLayout为开发者提供了一种不同于传统对于UI元素位置指定的布局方法。以前,不论是在IB里拖放,还是在代码中写,每个UIView都会有自己的frame属性,来定义其在当前视图中的位置和尺寸。而使用AutoLayout,就变为了使用约束条件来定义view的位置和尺寸 。


在AutoLayout中主要使用的类是NSLayoutConstraint

约束定义了两个视图对象之间的关系,这种关系可以描述为:

y (relation) m * x + b 简记为 y R m * x + b  R可以是==    >=   <=

比如两个视图View A 和View B ,View B的左边起始在View A 右边起始位置偏移 15 的位置

关系可以描述为:View B’s left = View A’s right * 1.0+ 15

又或者View B的宽度是 View A宽度的一半

关系为:View B's width = View A's width * 0.5 + 0 


NSLayoutConstraint的初始函数1:

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

上面该方法 用英语描述的意思是  view1 的属性 关系 view2的属性的 multiplier 倍数 加上偏移

值得注意的是 view2 可以是nil 这样该公式也就变成 y R b

然后添加约束的对象一般是view1和view2的最近公有父视图


假如给View 1和View 2确定约束关系,那么关系是被添加进View 3。

如果给View 1和View 3确定约束关系,那么关系是被添加进View 3。 (可以自己是自己的父视图)

View 3和View 4不能确定约束关系 因为没有公有父视图。


下面来看一个例子,没有使用frame来确定绝对位置,而是靠的constraints确定的相对关系

效果图:并没有使用任何绝对定位布置完成



附代码 大部分常用语句使用了宏替换

#import "ViewController.h"

@interface ViewController ()    
@property (nonatomic,strong) UIView *view1;
@property (nonatomic,strong) UIView *view2;
@property (nonatomic,strong) UIView *view3;
@end

@implementation ViewController
#define PRELAYOUT(View) View.translatesAutoresizingMaskIntoConstraints = NO
#define ADDBORDER(View) {View.layer.cornerRadius=10;View.layer.borderWidth=2;View.layer.borderColor=[UIColor blackColor].CGColor;}
#define SETWIDTH(View)  [NSLayoutConstraint constraintWithItem:View attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeWidth multiplier:1.0/3.0 constant:0]
#define SETHEIGHT(View) [NSLayoutConstraint constraintWithItem:View attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeHeight multiplier:1.0/12.0 constant:0]
#define ADDCONSTRAINT() [self.view addConstraint:constraint]
- (void)viewDidLoad {
    [super viewDidLoad];
    //basic init view
    self.view.backgroundColor = [UIColor whiteColor];
    _view1 = [UIView new];
    _view1.backgroundColor = [UIColor orangeColor];
    PRELAYOUT(_view1);
    _view2 = [UIView new];
    _view2.backgroundColor = [UIColor greenColor];
    PRELAYOUT(_view2);
    _view3 = [UIView new];
    _view3.backgroundColor = [UIColor blueColor];
    PRELAYOUT(_view3);
    [self.view addSubview:_view1];
    [self.view addSubview:_view2];
    [self.view addSubview:_view3];
    NSLayoutConstraint *constraint = nil;
    //view1's  center is at self.view's (1*center.x,2.0/3.0*center.y)
    //view1's centerX = self.view's centerX * 1.0 + 0
    constraint = [NSLayoutConstraint constraintWithItem:_view1 attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0];
    ADDCONSTRAINT();
    //view1's centerY = self.view's cneterY * 2/3 + 0
    constraint = [NSLayoutConstraint constraintWithItem:_view1 attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterY multiplier:2.0/3.0 constant:0];
    ADDCONSTRAINT();
    //view1's width is 1/3 self.view's width ,and height as 1/12 self.view's heigth
    //view1's width = self.view's width * 1/3 + 0
    constraint = SETWIDTH(_view1);
    ADDCONSTRAINT();
    //view1's height = self.view's heigth * 1/12 + 0
    constraint = SETHEIGHT(_view1);
    ADDCONSTRAINT();
    ADDBORDER(_view1)
    //view2 is relate to view 1 and same size as view 1
    //view2's centerX = view1's left * 1 - 20
    constraint = [NSLayoutConstraint constraintWithItem:_view2 attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:_view1 attribute:NSLayoutAttributeLeft multiplier:1.0 constant:-20];
    ADDCONSTRAINT();
    //view2's centerY = view1's bottom * 1 + 80
    constraint = [NSLayoutConstraint constraintWithItem:_view2 attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:_view1 attribute:NSLayoutAttributeBottom multiplier:1.0 constant:80];
    ADDCONSTRAINT();
    constraint = SETWIDTH(_view2);
    ADDCONSTRAINT();
    constraint = SETHEIGHT(_view2);
    ADDCONSTRAINT();
    ADDBORDER(_view2)
    //view 3 is relate to view 2 and same size as view 1
    //view3's left = view2's right * 1 + 40
    constraint = [NSLayoutConstraint constraintWithItem:_view3 attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:_view2 attribute:NSLayoutAttributeRight multiplier:1.0 constant:40];
    ADDCONSTRAINT();
    //view3's centerY = view2's centerY * 1 +0
    constraint = [NSLayoutConstraint constraintWithItem:_view3 attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:_view2 attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:0];
    ADDCONSTRAINT();
    constraint = SETWIDTH(_view3);
    ADDCONSTRAINT();
    constraint = SETHEIGHT(_view3);
    ADDCONSTRAINT();
    ADDBORDER(_view3)

}


@end

NSLayoutConstraint的初始函数2:

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

有一种更简易的方式提供了AutoLayout,那就是VFL(Visual Format Layout)。

以单纯的语句抽象出各视图间的关系。

format是 VFL字符串 具体规定了如何布局

opts 则是布局以何种方式对齐

metrics 则是在字符串中出现的常量字典

views 则是字符串中出现的视图字典


VFL字符串的结构可以表示为

(<orientation>:)? (<superview><connection>)? <view>(<connection><view>)* (<connection><superview>)?

?表示括号内内容可以出现0次或者多次

<oreitentation:>? :规定了布局的方向 分别为V:垂直 从上而下 H:水平 从左至右

<superview>: 用"|"表示 表示字符串中出现视图的父视图

<connection>:可以用不写 即为空 为空 :则下一个视图紧贴着之前视图的右边布局

单个"-"表示,表示预留系统默认间隔 子视图之间(siblings)同级之间 间隔8   子视图和父视图之间间隔为 20

用"- n -"表示,n代表了常数 或者常数名,表示视图间间隔n距离

<view>:用"[viewname]" 来表示 视图 用viewname 就是视图的名字


比如H:|-[view1]-20-[view2]|

可以解释为 在水平方向上 从左至右

①view1间隔在父视图左边的默认距离出布局

②view1 和 view2 之间间隔 20个距离

③view2 紧贴父视图的右边

通过以上基本上就可以确定了位置,但是view1 和view2的宽度 是不定的取决于系统

如果你想要view1 至少100宽,至多150

那么就可以写作如下形式

H:|-[view1(>=100,<=150)]-20-[view2]|

又或者你想要view1和view2 一样宽 可以写作

H:|-[view1(view2)]-20-[view2]|

如果要完成和之前类似的布局 用VFL该怎么做呢?

效果图:


代码:

#import "ViewController.h"

@interface ViewController ()
@property (nonatomic,strong) UIView *view1;
@property (nonatomic,strong) UIView *view2;
@property (nonatomic,strong) UIView *view3;
@end
#define PRELAYOUT(VIEW) VIEW.translatesAutoresizingMaskIntoConstraints = NO
#define ADDBORDER(VIEW) {VIEW.layer.cornerRadius=10;VIEW.layer.borderWidth=2;VIEW.layer.borderColor=[UIColor blackColor].CGColor;}
#define ADDCONSTRAINTS() [self.view addConstraints:constraints]
#define CONSTARINTS(FORMAT)  constraints =[NSLayoutConstraint constraintsWithVisualFormat:FORMAT options:0 metrics:nil views:views]
#define VIEWINIT(VIEW,COLOR) {VIEW = [UIView new];VIEW.backgroundColor = COLOR;PRELAYOUT(VIEW);ADDBORDER(VIEW);}

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    //basic init view
    self.view.backgroundColor = [UIColor whiteColor];
    VIEWINIT(_view1, [UIColor orangeColor])
    VIEWINIT(_view2, [UIColor greenColor])
    VIEWINIT(_view3, [UIColor blueColor])
    [self.view addSubview:_view1];
    [self.view addSubview:_view2];
    [self.view addSubview:_view3];
    //    NSLayoutConstraint *constraint = nil;
    NSArray *constraints = nil;
    NSDictionary *views = NSDictionaryOfVariableBindings(_view1,_view2,_view3);
    CONSTARINTS(@"V:|-180-[_view1(==50)]");
    ADDCONSTRAINTS();
    CONSTARINTS(@"H:|-130-[_view1]-130-|");
    ADDCONSTRAINTS();
    CONSTARINTS(@"V:[_view1]-50-[_view2(_view1)]");
    ADDCONSTRAINTS();
    CONSTARINTS(@"V:[_view1]-50-[_view3(_view1)]");
    ADDCONSTRAINTS();
    CONSTARINTS(@"H:|-50-[_view2]-40-[_view3(_view2)]-50-|");
    ADDCONSTRAINTS();

}
@end

使用VFL可能在精细度上没有前一种方式高,但是优点在于可视直观快速便捷。


在使用代码自动布局的过程中强烈推荐宏定义 这样就可以省去很多事。

也可以大大压缩代码

不过编译时慢,对运行无影响。

这里一个系统内部宏

#define NSDictionaryOfVariableBindings(...) _NSDictionaryOfVariableBindings(@"" # __VA_ARGS__, __VA_ARGS__, nil)
是用来根据视图名字创建视图的字典映射

比如NSDictionaryOfVariableBindings(_view1,_view2,_view3);

所创建出来的字典就是@{"_view1":_view1,"_view2":_view2,"_view3":_view3}

简单但说就是在解析VFL语句的时候 替换掉其中的_view1这些字符的


顺便一说metrics 也是可以这么用的 在metrics字典中存在"width":100,"spacing":20

如果在VFL语句中使用 就是下面的形式

H:[view1(==width)]-spacing-[view2(==width)]

等同于

H:[view1(==100)]-20-[view2(==100)]


VFL和之前约束是等同的,只是提供了一种快速创建约束的方式


最后来讲优先级的问题

enum {
UILayoutPriorityRequired = 1000, // Required 

UILayoutPriorityDefaultHigh = 750, // Compression resistance default 

UILayoutPriorityDefaultLow = 250, // Compression hugging default 

UILayoutPriorityFittingSizeLevel = 50, // System layout fitting size
};
typedef float UILayoutPriority;

优先级可以从1-1000

自动布局总是优先级比较高的布局 我们所创建的约束默认都是1000的优先级。

当两个约束冲突的时候,就会先满足高优先级的。

优先级的数值可以自己定义 比如751 999 等




最后再来一个VFL字符串的总结:

类型格式 例子
水平,垂直布局H: or V: V:[view1]-15-[view2]
视图[item][view1]
父视图| H:|[view1]|
关系== or >= or <=H:[view1]-(>=20)-[view2]
metricsmetricH:[view1(<=width)]
无间隔[item][item]H:[view1][view2]
弹性间隔[item]-(>=0)-[item][view1]-(>=0)-[view2]
固定间隔[item]-gap-[item]V:[view1]-20-[view2]
固定宽高[item(size)][item(==size)] [view1(50)]
优先级@value [view1(<=50@20)]
   
   











  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值