圆角与阴影
单纯的圆角应该没什么可说的。网上有很多设置圆角的方式。比如最简单的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被设置后就只负责渲染maskLayer内的内容,其余的部分一概不管。
另外使用mask也可以用来设置圆角,但是这样同样会产生离屏渲染的问题。很多博客都已经提到这个问题了,所以尽量不要这么做(读一声)。
minificationFilter和magnificationFilter
这两个属性决定了缩小和放大图片时的过滤器种类,有三个值可以选。
kCAFilterNearest//最近过滤算法
kCAFilterLinear//双线性滤波算法
kCAFilterTrilinear//三线性滤波算法
两个属性默认情况下是kCAFilterLinear,kCAFilterLinear和kCAFilterTrilinear在多数情况下显示效果没什么区别,kCAFilterNearest更适合于像素风格明显的图片使用。
这次就先坑到这。
网友评论