首页 > 代码库 > IOS开发之自动布局框架设计(二)

IOS开发之自动布局框架设计(二)

  【上集剧情概要:上集我们主要剖析了原生的NSLayoutConstraint实现自动布局的方式,我们知道是通过constraintWithItem这个初始化的方法来配备所需要的7个参数,然后通过addConstraint方法将布局添加进去,并且定义了

NSLayoutAttribute,NSLayoutRelation这些枚举】

如果我们自己设计一款布局框架可以怎么设计呢?

1.封装原有的NSLayoutConstraint类,可以将代码的实现更加简洁化。例如:Masonry,SDAutoLayout。

2.封装坐标布局,并且实现自有的布局方式,至于用什么方式展现给用户,可以自行天马行空,方便易用就行。

3.使用CSS/XML等方式,将布局文件放到一个统一的文件里面,然后初始化的时候进行加载布局。

4.开发一款软件,来实现拖拽好布局之后,自动生成代码,并且有面板可以设置微调当前的参数。

5.还可以将UI动画封装到布局里面,以及控件的交互的封装。

好吧,我们来看看如何封装好NSLayoutConstraint,其实它的核心代码是:

 1 NSLayoutConstraint *constaintTop = [NSLayoutConstraint 2                                         constraintWithItem:self.tableView 3                                         attribute:NSLayoutAttributeTop 4                                         relatedBy:NSLayoutRelationEqual 5                                         toItem:self.view 6                                         attribute:NSLayoutAttributeTop 7                                         multiplier:1.0 8                                         constant:20]; 9     10     [_tableView addConstraint:constaintTop];

那我们其实就是要封装这一层代码,并且实现更好地接口展现给用户。

1.实现链式调用

首先我们写一个最简单地实现UI布局的代码,功能:实现上下左右四个属性,使用链式调用。

如果我们要实现链式调用,怎么实现呢?

IOS中调用方法的实现使用的是【】,只有属性访问的时候才用点得方式,那么可以通过Setter方法吗?

比如返回值我们设置为当前的view

@property(nonatomic, readonly) UIView *leftSpace;-(UIView *)leftSpace{    return self;}

那后面的参数怎么传给它呢,所以它不能满足要求。

橘子君又想到一个问题,使用Block呢,因为block作为属性的时候是可以用这种方式的:

blockLeft(10);

也是可以通过.语法来访问的比如:

self.blockLeft(10);

那我们怎么来实现连续的链式点语法的操作呢

如果self.blockLeft(10);返回的是self的话,那就可以实现了:

self.blockLeft(10).blockTop(10).blockRight(10).blockBottom(10);

ok, 这样的话我们可以模拟实现一下这种链式调用的方式:

#import <UIKit/UIKit.h>@interface UIView (GCFAdditions)@property(nonatomic, readonly) UIView *(^left)(NSInteger space);@property(nonatomic, readonly) UIView *(^right)(NSInteger space);@property(nonatomic, readonly) UIView *(^top)(NSInteger space);@property(nonatomic, readonly) UIView *(^bottom)(NSInteger space);@end

首先简单模拟写了left,right,top,bottom四个block属性,返回值都为self

#import "UIView+GCFAdditions.h"@implementation UIView (GCFAdditions)- (UIView *(^)(NSInteger space))left{    return ^(NSInteger space) {        //code        if (space) {            NSLog(@"%ld",(long)space);        } else {            NSLog(@"%ld",(long)space);        }        return self;    };}- (UIView *(^)(NSInteger space))right{    return ^(NSInteger space) {        //code        if (space) {            NSLog(@"%ld",(long)space);        } else {            NSLog(@"%ld",(long)space);        }                return self;    };}- (UIView *(^)(NSInteger space))top{    return ^(NSInteger space) {        //code        if (space) {            NSLog(@"%ld",(long)space);        } else {            NSLog(@"%ld",(long)space);        }                return self;    };}- (UIView *(^)(NSInteger space))bottom{    return ^(NSInteger space) {        //code        if (space) {            NSLog(@"%ld",(long)space);        } else {            NSLog(@"%ld",(long)space);        }                return self;    };}@end

好,我们来仔细分析下上面的代码:

首先在属性里面声明一个block,返回类型为UIView,保证下一步的调用,给了一个参数,测试用的。

然后在实现里面,返回self,这时候就可以完成链式调用

#define SCREEN_WIDTH   [UIScreen mainScreen].bounds.size.width#define SCREEN_HEIGHT  [UIScreen mainScreen].bounds.size.height#import "ViewController.h"#import "Masonry.h"#import "UIView+GCFAdditions.h"@interface ViewController ()<UITableViewDelegate,UITableViewDataSource>@end@implementation ViewController- (void)viewDidLoad {    [super viewDidLoad];    // Do any additional setup after loading the view, typically from a nib.        self.tableView.left(50).right(50).top(50).bottom(50);    }- (void)didReceiveMemoryWarning {    [super didReceiveMemoryWarning];    // Dispose of any resources that can be recreated.}-(UITableView *)tableView{    if (!_tableView) {        _tableView=[[UITableView alloc]initWithFrame:CGRectMake(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT)];        _tableView.separatorStyle = UITableViewCellSeparatorStyleNone;        _tableView.backgroundColor=[UIColor redColor];        [self.view addSubview:_tableView];    }    return _tableView;}

打印结果:

2016-09-08 11:22:31.214 TestMansonry[298:16133] 502016-09-08 11:22:31.215 TestMansonry[298:16133] 502016-09-08 11:22:31.216 TestMansonry[298:16133] 502016-09-08 11:22:31.216 TestMansonry[298:16133] 50

 

2.实现最简单的布局

 

 

上面我们实现了链式调用,那么我们如何运用它来实现布局呢,请看如下代码:

 NSLayoutConstraint *constaintRight = [NSLayoutConstraint                                             constraintWithItem:self                                             attribute:NSLayoutAttributeRight                                             relatedBy:NSLayoutRelationEqual                                             toItem:self.superview                                             attribute:NSLayoutAttributeRight                                             multiplier:1.0                                             constant:-space];        [self.superview addConstraint:constaintRight];

 

我们知道实现布局的核心代码是这一段,我们直接移到之前的分类里面就会实现这种效果

- (UIView *(^)(NSInteger space))left{    return ^(NSInteger space) {        if (space) {            NSLog(@"%ld",(long)space);        } else {            NSLog(@"%ld",(long)space);        }                NSLayoutConstraint *constaintLeft = [NSLayoutConstraint                                            constraintWithItem:self                                            attribute:NSLayoutAttributeLeft                                            relatedBy:NSLayoutRelationEqual                                            toItem:self.superview                                            attribute:NSLayoutAttributeLeft                                            multiplier:1.0                                            constant:space];                [self.superview addConstraint:constaintLeft];                return self;    };}

 

在实现里面加入

self.tableView.left(50).right(50).top(50).bottom(50);

就可以实现上下左右各50的间距了,哈哈!

 

IOS开发之自动布局框架设计(二)