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);
        
    }];

}

运行的效果:


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) 想点什么,自己就往里面加什么方法, 是不是很爽

那这个是怎么实现的呢?

  • 首先我们来看一下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

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

推荐阅读更多精彩内容

  • iOS_autoLayout_Masonry 概述 Masonry是一个轻量级的布局框架与更好的包装AutoLay...
    指尖的跳动阅读 1,137评论 1 4
  • (一)Masonry介绍 Masonry是一个轻量级的布局框架 拥有自己的描述语法 采用更优雅的链式语法封装自动布...
    木易林1阅读 2,297评论 0 3
  • Masonry是一个轻量级的布局框架,拥有自己的描述语法,采用更优雅的链式语法封装自动布局,简洁明了并具有高可读性...
    3dcc6cf93bb5阅读 1,740评论 0 1
  • 下面主要介绍Kotlin在声明常量与变量这一块的变化,其完整的声明格式模板为: val 或 varval声明常量,...
    我想吃碗牛肉面阅读 284评论 0 0
  • 如果说我有第二次生命,真的是因为他。 在遭遇了被李可劈腿被甩然后我还乐呵呵的去找他,用最后那点卑微得可怜的自尊心去...
    猫耳朵yami阅读 176评论 0 0