经常有这样的需求,给view添加圆角,目前用过三种实现方式,第四种我也没用过。圆角总容易伴随着离屏渲染的问题
view.layer.cornerRadius = 50;
如果是给一个view 这种content为空的视图设置圆角,上面就达到了效果,如果是给UILabel 或者 UIButton 或者给CollectionView的一个item,contents不为空的视图设置圆角就不管用,需要两行代码共同使用
lab.layer.cornerRadius = 15;
lab.layer.masksToBounds = YES;
CAShapeLayer *layer = [CAShapeLayer layer];
layer.fillColor = [UIColor blueColor].CGColor;
layer.frame = CGRectMake(10, 500, 100, 100);
[self.view.layer addSublayer:layer];
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:layer.bounds byRoundingCorners:UIRectCornerTopLeft | UIRectCornerTopRight cornerRadii:CGSizeMake(12, 12)];
layer.path = path.CGPath;
UIView *v2 = [[UIView alloc] initWithFrame:CGRectMake(10, 350, 100, 100)];
v2.backgroundColor = [UIColor orangeColor];
[self.view addSubview:v2];
UIBezierPath *maskBezierPath = [UIBezierPath bezierPathWithRoundedRect:v2.bounds byRoundingCorners:UIRectCornerTopLeft | UIRectCornerTopRight cornerRadii:CGSizeMake(12, 12)];
CAShapeLayer *maskLayer = [CAShapeLayer layer];
maskLayer.frame = v2.bounds;
maskLayer.path = maskBezierPath.CGPath;
v2.layer.mask = maskLayer;
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];
imageView.image = [UIImage imageNamed:@"font"];
UIGraphicsBeginImageContextWithOptions(imageView.bounds.size, NO, [UIScreen mainScreen].scale);
[[UIBezierPath bezierPathWithRoundedRect:imageView.bounds cornerRadius:imageView.frame.size.width] addClip];
[imageView drawRect:imageView.bounds];
imageView.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
imageView.backgroundColor = [UIColor redColor];
[self.view addSubview:imageView];
mask 会触发离屏渲染(GPU在当前屏幕缓冲区外新开辟一个渲染缓冲区进行工作),带来额外的性能损耗,如果这样的圆角操作到达一定的数量,会触发缓冲区的频繁合并和上下文的频繁切换,性能的代价会宏观的表现在用户体验上,出现掉帧现象,不建议使用
第一种方式适用于页面中圆角比较少的情况,例如只设置一个头像这种;使用贝塞尔曲线和CAShapeLayer设置圆角是修改视图的layer层,这种方式对性能的损耗甚至超过了第一种方式,拒绝使用;使用贝塞尔曲线和CoreGraphics框架画出一个圆角,不会改变layer层, 也能高效的添加圆角,适用于给大量的控件添加圆角; 这些我没有写demo,有时间在现有的项目中写demo测一下,实时观测一下fps的变化。
涉及到贝塞尔曲线的都可以设置局部圆角
其实今天的主题是 使用Masonry 布局后不能立即获取到frame,从需求角度扩展出来上面的内容,就把之前注意到的东西总结一下,话说,记忆不好怎么办,是理解不够深刻吗,总结一遍又一遍,总记不住,效率贼低
经常在项目里面遇到这样的问题,使用masonry设置好布局之后,需要给某view添加渐变效果,使用CAGradientLayer来实现,但是直接使用上面布局view.bounds发现根本没办法获取到正确的bounds,基本上获取到的都是0.但是如果调用[view.superview layoutIfNeeded] 或者延迟一会再获取frame,就能获取到正确的frame,所以得出结论,masonry布局是需要时间的。autolayout最终也是转成frame,masonry是建立在autolayout上,没有获取到正确的值,是因为约束还没有布局完成,这需要一个过程, 在viewdidappear 或者viewdiddlayout里面都能拿到,最好还是在控制器的 viewdidlayout里面获取,因为autolayout会根据约束,不停的去改变frame,这方法里面最后拿到的frame 就是最终的姿势。因为调用[view.superview layoutIfNeeded]会回调 viewDidLayoutSubviews方法,所以最后能获取到正确的frame。看大神的资料说,其实不延迟,直接dispatch 0s 也可以获取到正确的frame,这是跟runloop有关的内容,找个话题继续详聊
以前没有用过UIGraphicsBeginImageContextWithOptions,在调研这个函数的使用的时候遇到这个案例,遇到一个这样的案例
下面是一个UI展示,用一个label 就展示完了,清晰明了,注意中间的条,中间的条使用CAShapeLayer创建的, 首先使用CAShapeLayer 创建一个layer出来,和贝塞尔曲线一起使用,画一条带颜色的线(layer),然后把layer转成渲染成图片,再使用富文本把图片加载为一个NSAttributeString,在需要的地方插入即可
获取图片上下文,有两个方法
// 参数: 指定将来创建出来的bitmap的大小
UIGraphicsBeginImageContext(CGSize size);
/*
* 参数一: 指定将来创建出来的bitmap的大小
* 参数二: 设置透明YES代表透明,NO代表不透明
* 参数三: 代表缩放,0代表不缩放
*/
UIGraphicsBeginImageContextWithOptions(CGSize size, BOOL opaque, CGFloat scale);
使用步骤:获取图片上下文-绘图-从图片上下文中获取绘制的图片-关闭图片上下文