iOS开发知识小集 iOS进阶之MasonryiOS Review

iOS 如何在用masonry布局时第一时间获取到frame

2018-06-29  本文已影响478人  George2016

问题:在用masonry布局时,想给按钮添加一个渐变的背景色。
渐变背景色用CAGradientLayer来创建。

一、 CAGradientLayer的介绍

CAGradientLayer是CALayer的子类,可以直接将设置好的图层添加到UIView的layer上,使用方便。

// 渐变颜色的数组(CGColorRef对象)
@property(nullable, copy) NSArray *colors;
// 渐变颜色位置,[0-1]范围,递增,数量和colors数量相等,否则无效
@property(nullable, copy) NSArray<NSNumber *> *locations;
// 渐变的起点和终点,[0,0]-[1,1]
// [0,0]是左下角,[1,1]是右上角。默认值分别为[.5,0]和[.5,1]
@property CGPoint startPoint;
@property CGPoint endPoint;
// 将绘制的渐变类型。 目前唯一允许值为“轴”(默认值)可以忽略
@property(copy) NSString *type;

二、Masonry的原理

首先你要知道autolayout和frame的关系,autolayout最终也是转成frame,masonry是建立在autolayout之上的。你没获取到正确的值,那是因为约束还没布局完成。相当于就是我们给一定的约束,系统内部自己去根据约束条件转成对应的frame,而这需要一个过程。想要拿到正确的frame最好的就是让autolayout完成之后,什么时候完成呢?那就是在layoutsubviews for view or didlayoutsubviews for controller 里获取,当然在控制器的viewdidappear里也拿得到,但是正确做法和最佳做法还是在控制器里的viewdidlayout里获取最好~因为autolayout会根据约束,不停的去改变frame,这方法里最后拿到的frame就是最终姿势.

那么约束什么时候能完成frame的转换呢?
答案:下一帧的时候。

如此一来,我们便想到用dispatch_after这个函数来解决这一问题了。只要我们延时0秒即可。

这里可能有人要懵逼了,延时0秒和没延时有啥区别?

block里面的执行其实已经是下一帧了

完整代码

//ViewController.m
- (void)viewDidLoad {
    [super viewDidLoad];

    self.view.backgroundColor = [UIColor lightGrayColor];
    
    UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
    [button setTitle:@"点我试试" forState:UIControlStateNormal];
    [self.view addSubview:button];
    [button mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.right.equalTo(button.superview).inset(12);
        make.height.mas_equalTo(44);
        make.bottom.equalTo(self.view).offset(-20);
    }];
    
    CAGradientLayer *gradientLayer = [CAGradientLayer new];
    gradientLayer.colors = @[(id)[UIColor redColor].CGColor,(id)[UIColor greenColor].CGColor,(id)[UIColor blueColor].CGColor];
    gradientLayer.startPoint = CGPointMake(0, 0);
    gradientLayer.endPoint = CGPointMake(1, 0);
    [button.layer addSublayer:gradientLayer];
    
    didLayout(^{
        gradientLayer.frame = button.bounds;
    });
}
//ViewController.m
//定义一个C函数,用来简单封装一下dispatch_after
void didLayout(void(^layout)(void)) {
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        if (layout) layout();
    });
}
上一篇下一篇

猜你喜欢

热点阅读