CALayer常用属性整理(三)

圆角与阴影

单纯的圆角应该没什么可说的。网上有很多设置圆角的方式。比如最简单的layer.cornerRadius,图层蒙版或者CAShapeLayer。

在使用cornerRadius属性时,他只会作用于前景框和背景色,对于contents有内容的图层或者内部还有子视图的控件(比如UILabel)只单一的设置这个属性是不会显示出圆角效果的。多数情况下我们会这样写

layer.cornerRadius = 5;
layer.masksToBounds = YES;

这样做很显然会导致离屏渲染,当然圆角数量不多的情况下根本不需要在乎这个问题。但是在滚动视图比如table或者collection上如果也需要使用cornerRadius和masksToBounds的话就需要再加上两句话来挽救我们的帧数。

layer.shouldRasterize = YES;
layer.rasterizationScale = [UIScreen mainScreen].scale;

上面两行代码会将把视图内的内容进行缓存。这样做的弊端也很明显,一旦视图的内容被改变缓存就失去意义了。

还有一些为视图添加圆角的方法。这里举出一个为图片添加圆角的例子(原文
- (UIImage*)imageAddCornerWithRadius:(CGFloat)radius andSize:(CGSize)size{
CGRect rect = CGRectMake(0, 0, size.width, size.height);

  UIGraphicsBeginImageContextWithOptions(size, NO, [UIScreen   mainScreen].scale);
  CGContextRef ctx = UIGraphicsGetCurrentContext();
UIBezierPath * path = [UIBezierPath bezierPathWithRoundedRect:rect byRoundingCorners:UIRectCornerAllCorners cornerRadii:CGSizeMake(radius, radius)];
  CGContextAddPath(ctx,path.CGPath);
  CGContextClip(ctx);
  [self drawInRect:rect];
  CGContextDrawPath(ctx, kCGPathFillStroke);
  UIImage * newImage = UIGraphicsGetImageFromCurrentImageContext();
  UIGraphicsEndImageContext();
  return newImage;
}

原谅我把代码厚颜无耻的直接复制过来了(hiahia),使用时把这个方法加在UIImage的extends里面然后这样调用就可以了。

UIImageView *imageView = [[UIImageView alloc] initWithImage:[[UIImage imageNamed:@"avatar.jpg"] imageAddCornerWithRadius:15 andSize:CGSizeMake(200, 200)]];

CALayer有五个关于阴影的属性,将图层的shadowOpacity属性设置为一个大于0小于1的值时将会在图层下出现阴影。shadowColor设置阴影颜色。

shadowOffset是一个CGSize值,默认是(0,-3)。就是阴影会出现在图层上方。至于为什么会是上方,还是和Mac OS有关,因为Mac OS和iOS的Y轴是相反的。shadowRadius负责调整阴影的模糊度,默认是3.0,数值越大越模糊。shadowPath用来指定一个阴影的绘制形状。此外shadowPath的另外一个用处是解决离屏渲染的问题,设置图层阴影会导致离屏渲染的发生,过多的阴影会影响到性能甚至用户体验。我们可以在设置阴影的代码后面加上这样一句

layer.shadowPath = [UIBezierPath bezierPathWithRect:layer.bounds].CGPath;

通过手动指定阴影路径,从而避免Core Animation自动计算引起的离屏渲染。

关于圆角和阴影还有一个很古老的问题,就是给一个圆角视图添加阴影。关于这个问题的出现还是说一下吧,万一有萌新不知道呢- -、。

当我们使用cornerRadius给视图添加圆角时,很多时候因为contents里面有内容,所以不得不使用masksToBounds来将多余的部分裁剪掉。如果这个时候这个视图被设置了阴影属性,那么阴影也将会被视为多余的部分被一起裁剪。这踏马就很尴尬了,要阴影就不能设置masksToBounds,不设置masksToBounds就没有圆角。

这个问题有不止一个解决办法。最直观的办法就是把原来的一层layer换成两层,一层负责阴影和圆角,另一层负责显示内容,这样就不会再有冲突。

    UIView *shadowView = [[UIView alloc] initWithFrame:CGRectMake(10, 250, 150, 150)];
  shadowView.layer.shadowOpacity = 0.5;
  shadowView.layer.cornerRadius = 15;
  shadowView.layer.shadowOffset = CGSizeMake(15, 15);
  shadowView.backgroundColor = [UIColor whiteColor];
  [self.view addSubview:shadowView];

  CALayer *shadowLayer = [CALayer layer];
  shadowLayer.frame = shadowView.bounds;
  shadowLayer.cornerRadius = 15;
  shadowLayer.masksToBounds = YES;
  shadowLayer.contents = (__bridge id _Nullable)([UIImage imageNamed:@"avatar.jpg"].CGImage);
  [shadowView.layer addSublayer:shadowLayer];

首先创建一个View,在View自带的layer上添加阴影属性和设置圆角(在上面提到过,单纯的设置在只有背景色或者前景框的情况下设置圆角是不需要masksToBounds配合的,因为contents里面并没有内容)。这时contents属性为nil,所以圆角和阴影是可以并存的。然后我们再创建一个新的图层(CALayer),在这层上加入图片并且设置圆角。这样就达到了圆角加阴影的效果。

文章开始就提到过使用cornerRadius+ masksToBounds的方式会引起离屏渲染,所以我们可以利用下上面我从大神那没脸没皮直接抄过来的代码。把shadowLayer改写如下:

CALayer *shadowLayer = [CALayer layer];
shadowLayer.frame = shadowView.bounds;
shadowLayer.contents = (__bridge id _Nullable)([[UIImage imageNamed:@"avatar.jpg"] imageAddCornerWithRadius:15 andSize:shadowLayer.bounds.size].CGImage);
[shadowView.layer addSublayer:shadowLayer];

图层蒙版(mask)

关于mask的解释直接看演示效果会更直观。

UIImageView *image = [[UIImageView alloc] initWithFrame:CGRectMake(10, 250, 150, 150)];
image.image = [UIImage imageNamed:@"avatar.jpg"];
[self.view addSubview:image];

CALayer *maskLayer = [CALayer layer];
maskLayer.frame = image.bounds;
maskLayer.contents = (__bridge id _Nullable)([UIImage imageNamed:@"add"].CGImage);
image.layer.mask = maskLayer;

首先创建了一个ImageView,并且放上一张这样的图片。


avatar.jpg

然后创建一个图层,在图层上放一张这样的图片(项目里拖出来的,凑合用)。


add.png

都添加好后在代码最后一句将放有加号的layer作为mask属性赋给imageView。
这时就会看到这样的效果。

mask效果图.png

这样应该就很好理解了,mask被设置后就只负责渲染maskLayer内的内容,其余的部分一概不管。

另外使用mask也可以用来设置圆角,但是这样同样会产生离屏渲染的问题。很多博客都已经提到这个问题了,所以尽量不要这么做(读一声)。

minificationFilter和magnificationFilter

这两个属性决定了缩小和放大图片时的过滤器种类,有三个值可以选。

kCAFilterNearest//最近过滤算法
kCAFilterLinear//双线性滤波算法
kCAFilterTrilinear//三线性滤波算法

两个属性默认情况下是kCAFilterLinear,kCAFilterLinear和kCAFilterTrilinear在多数情况下显示效果没什么区别,kCAFilterNearest更适合于像素风格明显的图片使用。

这次就先坑到这。

参考文章:
iOS 高效添加圆角效果实战讲解
UIKit性能调优实战讲解
小心别让圆角成了你列表的帧数杀手

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

推荐阅读更多精彩内容