关于iOS Autolayout自动布局(纯代码Format字符串法)

谈自动布局,那基本的视图是第一步,做了一个这样的ViewController

#import "NESMainViewController.h"
@interface NESMainViewController ()
@property (nonatomic,retain) UIView *view1;
@property (nonatomic,retain) UIView *view2;
@property (nonatomic,retain) UIView *view3;
@end

@implementation NESMainViewController
-(UIView *)view1
{
if (!_view1) {
    _view1 = [[UIView alloc] initWithFrame:CGRectMake(10, 30, 145, 200)];
    _view1.backgroundColor = [UIColor greenColor];
}
return _view1;
}

-(UIView *)view2
{
if (!_view2) {
    _view2 = [[UIView alloc] initWithFrame:CGRectMake(165, 30, 145, 200)];
    _view2.backgroundColor = [UIColor yellowColor];
}
return _view2;
}

-(UIView *)view3
{
if (!_view3) {
    _view3 = [[UIView alloc] initWithFrame:CGRectMake(10, 240, 300, 300)];
    _view3.backgroundColor = [UIColor blueColor];
}
return _view3;
}

-(void)buildLayout
{
[self.view addSubview:self.view1];
[self.view addSubview:self.view2];
[self.view addSubview:self.view3];
}

- (void)viewDidLoad
{
[super viewDidLoad];
[self buildLayout];
}

- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}

@end

看完这个类,有必要先说一下我的代码风格,对于很多刚刚做iOS开发不久的程序员看这部分代码可能感觉很麻烦~但是按照这种风格进行编码是有很大好处的.
先看这段代码

-(UIView *)view1
{
if (!_view1) {
    _view1 = [[UIView alloc] initWithFrame:CGRectMake(10, 30, 145, 200)];
    _view1.backgroundColor = [UIColor greenColor];
}
return _view1;
}

这段代码的写法经常能够看到,单例里面有他,TableView的代理方法能用到他,这里属于重写@property的getter方法,简单来说,通过点语法来调用私有成员变量:self.view1来调用该方法,有两大好处

  • 分离了不同方法的构造内容,代码层次更加明显.
  • 延迟加载,什么时候需要什么时候创建,而不是统一在ViewDidLoad方法中进行创建,对于复杂的视图控制器来说可以优化运行效率.

再看ViewDidLoad方法里

- (void)viewDidLoad
{
[super viewDidLoad];
[self buildLayout];
}

非常简单,调用了一个buildLayout方法,看方法名就能够知道,这里是专门用来初始化视图布局的方法,由于在oc中init开头的方法被看做类的初始化方法,故此使用了build,当然这属于个人习惯,无所谓的事~

buildLayout方法则逐个将要添加的控件放到了view上

-(void)buildLayout
{
[self.view addSubview:self.view1];
[self.view addSubview:self.view2];
[self.view addSubview:self.view3];
}

这里就能够明显的看到通过重写getter方法来初始化视图的好处了,都添加了哪些控件一目了然,如果需要修改某一个控件,那直接定位到对应的getter方法修改即可,而无需在大量的代码中搜索那么几行代码.

根据这样的代码布局进行编写,对于单一视图控制器来说,就可以有了这样的分层效果:


闲话到这,继续主题.
代码到这里屏幕上的视图应该如下图所示



是我们需要的布局效果,但是如果屏幕横过来,问题就出现了


接下来就需要对代码进行一定的调整来完成自动布局.

-(UIView *)view1 {
 if (!_view1) {
 _view1 = [[UIView alloc] initWithFrame:CGRectMake(10, 30, 145, 200)];
 _view1.backgroundColor = [UIColor greenColor]; 
} 
return _view1; 
}

以及其他方法中添加如下代码:

_view1.translatesAutoresizingMaskIntoConstraints = NO;

用来禁止AutoresizingMask转换成AutoLayout,简单来说,Autoresizing和AutoLayout用的不是一套东西,但是默认情况下是相互转换的,这里我们要指定使用AutoLayout系统,所以要禁止自动转换

跟着在buildLayout方法中添加下列内容:

NSDictionary *views = NSDictionaryOfVariableBindings(self.view,_view3,_view2,_view1);

[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"|-10-[_view1(==_view2)]-10-[_view2]-10-|" options:0 metrics:0 views:views]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-30-[_view1(<=200)]-10-[_view3]-10-|" options:0 metrics:0 views:views]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-30-[_view2(<=200)]-10-[_view3]-10-|" options:0 metrics:0 views:views]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"|-10-[_view3]-10-|" options:0 metrics:0 views:views]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[_view3(>=150)]-10-|" options:0 metrics:0 views:views]];

一步一步看:

NSDictionary *views = NSDictionaryOfVariableBindings(self.view,_view3,_view2,_view1);

这里用到了一个系统宏定义,NSDictionaryOfVariableBindings(),其作用是生成一个词典,key的名字和对象的标识符相同,以上述为例,生成的词典形式就是{"self.view":self.view,@"_view3":_view3,...},这个词典应当包含需要自动布局的父视图和所有的子视图,

[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"|-10-[_view1(==_view2)]-10-[_view2]-10-|" options:0 metrics:0 views:views]];

这里则是添加了一条自动布局规则,可以看到有个比较麻烦的字符串参数,简单解释一下

| 屏幕的边框
-10- 10个点的间距
[_view2] 需要布局的控件,这里的_view2必须和上边用来生成词典的标识符完全相同
[_view1(==_view2)] _view1的作用和上边完全相同,而()里面的则是限定条件,可以是==某一个空间,或者一个固定的数值,当然,除了==之外还可以使用>=或者<=;

字符串中的每个部分都解释清楚后,那么就容易理解多了~这句话完整的意思整理出来就是:

两个宽度相同的view,其间距是10个点,距离左右边框的距离也是10个点~

这样,无论是竖屏还是横屏,都会按照这样一个标准来去进行自动布局.

值得注意的是,能够执行这种布局的view必须通过
-(CGSize)intrinsicContentSize方法返回一个有效的CGSize,而UIView默认返回的是0,所以如果只设置了横向的布局规则那么UIView是不会显示在屏幕上的

对于UIView而言,必须同时设置横向和纵向布局规则或者写一个继承自UIView的自定义View然后重写-(CGSize)intrinsicContentSize才能实现该效果.

但是对于UIButton,UILabel等则不必,设置个横向的就够了.

再看下一句:

V:|-30-[_view1(<=200)]-10-[_view3]-10-|

V: 代表的是垂直方向上的布局规则

简单解释一下,view1的高度不能大于200点,距离上边框30个点,与view3的间距是10个点,view3与底边框的距离是10个点

由此,通过设置view1的高度上限和间距就可以自动计算出view3的高度.其他的布局规则大家自己看一下也就可以明白了.

同时,如果规定了全部子视图的布局规则,那么也就没有必要去设置子视图的frame了.各个frame的初始化方法可以改写成:

-(UIView *)view1
{
if (!_view1) {
    _view1 = [[UIView alloc] init];
    _view1.backgroundColor = [UIColor greenColor];
    _view1.translatesAutoresizingMaskIntoConstraints = NO;
}
return _view1;
}

由此便完成了整个视图的自动布局,还是非常容易实现的,就个人而言,通过代码来进行自动布局比xib要方便的多.大家可以自行选择.

如果在view1中需要添加子view同样需要自动布局呢?看看下列代码,非常简单:

-(UIView *)view1
{
if (!_view1) {
    _view1 = [[UIView alloc] init];
    _view1.backgroundColor = [UIColor greenColor];
    _view1.translatesAutoresizingMaskIntoConstraints = NO;
    
    UIView *view = [[UIView alloc] init];
    view.backgroundColor = [UIColor magentaColor];
    [_view1 addSubview:view];
    view.translatesAutoresizingMaskIntoConstraints = NO;
    NSDictionary *views = NSDictionaryOfVariableBindings(_view1,view);
    [_view1 addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"|-10-[view]-10-|" options:0 metrics:0 views:views]];
    [_view1 addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-10-[view]-10-|" options:0 metrics:0 views:views]];
    
}
return _view1;
}

好了,到这里自动布局就差不多了,效果图如下:



最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容