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