码农的日常之iOS开发第三方库iOS程序员

Masonry框架浅析-链式编程

2016-07-07  本文已影响626人  过过过客
#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);
        
    }];

}

运行的效果:


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状态下:

Snip20160707_2.png

Selected状态下:

Snip20160707_3.png

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

那这个是怎么实现的呢?

    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];
}
@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对象

/**
 *  返回值为: 返回值为 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)

/**
 *  返回值为: 返回值是 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的作者封装的比较狠,理解起来困难还是大大的, 我这里也只是简单的介绍了一下这行代码的执行思路

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

上一篇下一篇

猜你喜欢

热点阅读