Masonry框架浅析-链式编程

  • Masonry框架github地址:

    https://github.com/SnapKit/Masonry

  • 首先我们来导入Masonry看下效果:
#import "ViewController.h"
#import "Masonry.h"



@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // 添加一个黄色的view
    [self addYellowView];
    
}



- (void)addYellowView {
    
    UIView *yellowView = [[UIView alloc]init];
    
    yellowView.backgroundColor = [UIColor yellowColor];
    
    [self.view addSubview:yellowView];
    
    // 设置约束
    [yellowView mas_makeConstraints:^(MASConstraintMaker *make) {
        
        // 设置顶部的约束 距self.view顶部为100
        make.top.equalTo(self.view).offset(100);
        
        // 设置左边的约束
        make.left.equalTo(self.view).offset(20);
        
        // 设置右边的约束
        make.right.equalTo(self.view).offset(-20);
        
        // 设置高
        make.height.equalTo(@50);
        
    }];

}

运行的效果:


Masonry框架浅析-链式编程_第1张图片
Snip20160707_1.png

于是乎,通过这个框架,我们在给UIButton设置一些属性的时候可以这样做:

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // 添加一个按钮
    [self addButton];
    
}

- (void)addButton {
    // 创建按钮
    UIButton *button = [[UIButton alloc]init];
    
    // 设置frame
    button.frame = CGRectMake(50, 100, 150, 50);
    
    // 绑定了点击事件
    [button addTarget:self action:@selector(buttonAction:) forControlEvents:UIControlEventTouchUpInside];
    
    // 设置属性
    [button lj_makeAttribute:^(LJButtonManager *make) {
        
        // 设置普通状态的图片 背景图片 文字颜色 文字内容
        make.normal.imgName(@"LJbtn_img_normal").bgImgName(@"LJbtn_bgImg_normal").color([UIColor redColor]).title(@"我是普通状态");
        
        // 设置选中状态的图片 背景图片 文字颜色 文字内容
        make.selected.imgName(@"LJbtn_img_selectedl").bgImgName(@"LJbtn_bgImg_selected").color([UIColor blueColor]).title(@"我是选中状态");
    }];
    // 添加
    [self.view addSubview:button];
}

- (void)buttonAction:(UIButton *)sender {
    // 切换按钮状态
    sender.selected = !sender.selected;
    
}

我们来看下运行之后的效果:
Normal状态下:

Masonry框架浅析-链式编程_第2张图片
Snip20160707_2.png

Selected状态下:

Masonry框架浅析-链式编程_第3张图片
Snip20160707_3.png

怎么样,是不是很爽,有时需要给button多个不同状态设置属性,可以这样点 点 点(.image.bgImage.color.title.frame) 想点什么,自己就往里面加什么方法, 是不是很爽

那这个是怎么实现的呢?

  • 首先我们来看一下masonry怎么实现的:
    UIView *yellowView = [[UIView alloc]init];
    
    yellowView.backgroundColor = [UIColor yellowColor];
    
    [self.view addSubview:yellowView];
    
    // 设置约束
    [yellowView mas_makeConstraints:^(MASConstraintMaker *make) {
        
        // 设置顶部的约束 距self.view顶部为100
        make.top.equalTo(self.view).offset(100);
        
        // 设置左边的约束
        make.left.equalTo(self.view).offset(20);
        
        // 设置右边的约束
        make.right.equalTo(self.view).offset(-20);
        
        // 设置高
        make.height.equalTo(@50);
        
    }];

我们com + 左键mas_makeConstraints: 到这个方法里面去看一下

// UIView 的分类
@implementation MAS_VIEW (MASAdditions)

/**
 *  添加约束的方法
 *
 *  @param block 无返回值 参数为 约束管理者对象的 block
 *
 *  @return 存有所有约束的数组
 */
- (NSArray *)mas_makeConstraints:(void(^)(MASConstraintMaker *))block {
    // 将view自带的约束设置为NO 避免冲突
    self.translatesAutoresizingMaskIntoConstraints = NO;
    // 创建约束管理者 并将 self 传进去   此时的self是 当前方法的调用者
    MASConstraintMaker *constraintMaker = [[MASConstraintMaker alloc] initWithView:self];
    // 调用传进来的的block
    block(constraintMaker);
    // 返回存有所有约束行为的数组
    return [constraintMaker install];
}
  • 这个类是UIView的一个分类

    • 我们的view在调用mas_makeConstraints:这个方法的时候,需要传递一个 无返回值,参数为MASConstraintMaker对象的block
      我们在调用这个方法的时候,需要传入这样子的一个block,并且给这个block赋值,赋值的过程就相当于我们在给view设置约束
  • 这个是怎么设置约束的呢

    • make.top.equalTo(self.view).offset(100);我们来看下这行代码,这行代码里面,这行代码里面,是通过make这个对象设置约束的,make.top表示给顶部添加约束

    • 那现在我们com + 左键到这个MASConstraintMaker里面看下:

@interface MASConstraintMaker : NSObject

/**
 *  The following properties return a new MASViewConstraint
 *  with the first item set to the makers associated view and the appropriate MASViewAttribute
 */
@property (nonatomic, strong, readonly) MASConstraint *left;
@property (nonatomic, strong, readonly) MASConstraint *top;
@property (nonatomic, strong, readonly) MASConstraint *right;
@property (nonatomic, strong, readonly) MASConstraint *bottom;
@property (nonatomic, strong, readonly) MASConstraint *leading;
@property (nonatomic, strong, readonly) MASConstraint *trailing;
@property (nonatomic, strong, readonly) MASConstraint *width;
@property (nonatomic, strong, readonly) MASConstraint *height;
@property (nonatomic, strong, readonly) MASConstraint *centerX;
@property (nonatomic, strong, readonly) MASConstraint *centerY;
@property (nonatomic, strong, readonly) MASConstraint *baseline;

这些就是我们需要view的那些位置设置约束,但是我们可以看到@property (nonatomic, strong, readonly) MASConstraint *top; 这个top位置属性是一个MASConstraint的对象,来到MASConstraintMaker.m文件

我们找到了这个top属性的get方法:

- (MASConstraint *)addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {
    
    // 执行添加约束的方法
    return [self constraint:nil addConstraintWithLayoutAttribute:layoutAttribute];
}

- (MASConstraint *)left {
    return [self addConstraintWithLayoutAttribute:NSLayoutAttributeLeft];
}

- (MASConstraint *)top {
    // 给view添加一个顶部位置的约束 返回值为方法调用者本身 这样又可以接着调用方法
    return [self addConstraintWithLayoutAttribute:NSLayoutAttributeTop];
}

发现这个方法最重执行的是[self constraint:nil addConstraintWithLayoutAttribute:layoutAttribute]这个方法,继续com + 左键进去,我们来到这个方法的实现:

- (MASConstraint *)constraint:(MASConstraint *)constraint addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {
    // 创建一个约束
    MASViewAttribute *viewAttribute = [[MASViewAttribute alloc] initWithView:self.view layoutAttribute:layoutAttribute];
    // 创建一个具体的约束的管理者 并把约束传过去
    MASViewConstraint *newConstraint = [[MASViewConstraint alloc] initWithFirstViewAttribute:viewAttribute];
    // 因为传过来的nil 所以不执行
    if ([constraint isKindOfClass:MASViewConstraint.class]) {
        //replace with composite constraint
        NSArray *children = @[constraint, newConstraint];
        MASCompositeConstraint *compositeConstraint = [[MASCompositeConstraint alloc] initWithChildren:children];
        compositeConstraint.delegate = self;
        [self constraint:constraint shouldBeReplacedWithConstraint:compositeConstraint];
        return compositeConstraint;
    }
    // 传过来的为nil 执行此方法
    if (!constraint) {
        // 设置代理
        newConstraint.delegate = self;
        // 将约束添加到存有约束的数组
        [self.constraints addObject:newConstraint];
    }
    // 返回具体的约束管理者
    return newConstraint;
}

我们可以看到这里面就是在进行设置约束的一些操作 ,最后的返回值是MASViewConstraint对象,至此,我们大概可以认为已经确定了要给viewtop设置约束,并且返回值是一个MASViewConstraint对象

  • 我们接着看这行代码make.top.equalTo(self.view).offset(100);,make.top是确定了给view的哪个位置设置约束,我们在来看看make.top.equalTo(self.view)这行代码,还是一样,com + 左键equalTo(self.view)里面:
/**
 *  返回值为: 返回值为 MASConstraint对象 参数为 id类型 的一个block
 */
- (MASConstraint * (^)(id))equalTo {
    
    // 返回一个block
    return ^id(id attribute) {
        
        // 给 view 的top 设置相对于 attribute 设置约束
        // 此时的 attribute 就是我们 make.top.equalTo(self.view).offset(100); 中的self.view
        return self.equalToWithRelation(attribute, NSLayoutRelationEqual);
    };
}

到这里,我们基本上就可以认为make.top.equalTo(self.view)这行代码执行就可以让 yellowViewtopself.viewtop0了(默认是0)

  • 接着,我们继续看make.top.equalTo(self.view).offset(100);,还是一样com + 左键offset(100):
/**
 *  返回值为: 返回值是 MASConstraint对象 ,参数是 CGFloat类型的 一个block
 */
- (MASConstraint * (^)(CGFloat))offset {
    // 返回block
    return ^id(CGFloat offset){
        // 设置偏移值
        self.offset = offset;
        // block的返回值  返回自己
        return self;
    };
}

到这里,make.top.equalTo(self.view).offset(100);这行代码就执行完了,这个里面还有很多步骤,由于Masonry的作者封装的比较狠,理解起来困难还是大大的, 我这里也只是简单的介绍了一下这行代码的执行思路

  • 最后我们总结一下

    • 为什么make.top.equalTo(self.view).offset(100); 可以这样子一直点点点 ;make.top相当于get方法,这个方法的返回值的对象本身- (MASConstraint *)top

    我们也可以写成这样:

            MASConstraint *make1 = make.top;
          
          MASConstraint *make2 = make1.equalTo(self.view);
          
          MASConstraint *make3 = make2.offset(100);
    

    接着每次调用get方法之后,我又可以拿到调用者本身,于是我们又可以接着调方法,就可以一直点点点了;
    然后就是make.top.equalTo(self.view).offset(100);这个括号里面的参数,.offset的返回值是一个MASConstraint * (^)(CGFloat)block; 我们在执行了make.top.equalTo(self.view).offset之后,就可以拿到这个block;可以写成这样:

            // 定义block
          MASConstraint *(^block)(CGFloat);
          
          // 拿到返回回来的block
          block = make.top.equalTo(self.view).offset;
          
          // 调用block
          block(100);
          
          // 拿到block的返回值
          MASConstraint *make = block(100);
    

    block作为参数的时候,这个block是由外部来实现,内部调用的

    block作为返回值的时候,这个block是由内部来实现,外部调用的

可能大家看到这里还是很多不明白,大家可以下一下我的demo,demo写的非常简单,你们下载之后,根据自己的理解,可以自己添加方法(比如.frame().titleEdgeInsets()(buttonframelabel缩进)),我也写了很多注释,相信能帮到你们,然后对demo有什么疑问的地方,或者有什么好的建议,希望大家联系我,共同探讨
写到这里,我也要结束装逼了,大家一起装逼,才是真的装逼
buttondemo的github地址:https://github.com/2098187liujing/-demo
ending

你可能感兴趣的:(Masonry框架浅析-链式编程)