欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

玩转iOS开发:4.《Core Animation》CALayer的视觉效果

程序员文章站 2024-03-24 22:00:58
...

文章分享至我的个人博客: https://cainluo.github.io/14775511877452.html


作者感言

前一章我们对CALayer了解的更加深入了一些《Core Animation》CALayer的几何图层今天我们就来讲讲CALayer Visual Effects, 也就是CALayer的视觉效果.

**最后:** **如果你有更好的建议或者对这篇文章有不满的地方, 请联系我, 我会参考你们的意见再进行修改, 联系我时, 请备注**`Core Animation`**如果觉得好的话, 希望大家也可以打赏一下~嘻嘻~祝大家学习愉快~谢谢~**

简介

CALayer Visual Effects讲得是CALayer一些我们能够看得见的东西, 这些知识点在我们日常开发中也会有用到的, 比如Rounded Corners, Layer Borders, Drop Shadows, Layer Masking, Scaling Filters, Group Opacity等等, 待我们一一去讲解.


Rounded Corners

Rounded Corners这个东西我们用的其实也是挺多的, 我们都知道, 在iOS 7之前, 基本上所有的Button都是椭圆形的, 而这些Button之所以都是椭圆形, 大多数都是因为Rounded Corners这个东西的原因. 在CALayer有一个叫conrnerRadius的CGFloat类型属性来控制着图层角的曲率, 默认值为0, 你可以设置任意数值, 是的图层角显示不一样的曲率, 而且conrnerRadius这个属性所影响的到的只有设置该属性的CALayer, 并不会影响到子图层或者是背景图, 但如果你要让子图层或者是背景图也要跟着该CALayer进行曲率处理, 你可以把masksToBounds设置为YES, 这样子就可以满足你的需求了. 我们直接来看看Demo吧:

- (void)layerRoundedCorners {
    
    UIView *view = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];
    
    view.backgroundColor = [UIColor redColor];
    view.layer.cornerRadius = 50.f;
    view.layer.masksToBounds = YES;
    
    CALayer *layer = [CALayer layer];
    
    layer.backgroundColor = [UIColor blueColor].CGColor;
    layer.frame = CGRectMake(50, 50, 50, 50);
    
    [view.layer addSublayer:layer];
    [self.view addSubview:view];
}
复制代码

PS: 这里需要注意一点, 由于masksToBoundsconrnerRadius这两个属性同时使用是挺消耗性能的, 如果你是要大面积的去使用, 会造成卡顿现象, 比如说在UICollectionView或者是UITableView上使用.


Layer Borders

CALayer还有两个更好玩的属性, 这也是在iOS 7之前Button会默认实现的两个属性, 一个叫做borderWidth, 一个叫做borderColor.

  • borderWidth: 这是一个CGFloat类型的属性, 是用来设置CALayer边框宽度, 默认值是为0.
  • borderColor: 这是一个CGColorRef类型的属性, 所以你不能给它直接设置一个UIColor对象, 前面我们已经对CGColorRef这个东东进行了简单的介绍, 这里我们只需要知道直接给它赋个值就好了, 还有就是borderColor是用来设置CALayer边框颜色, 在iOS 7之前默认值是为黑色, iOS 7之后默认值是为透明.
我们直接来看Demo吧:
- (void)layerBorders {
    
    UIView *view = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];
    
    view.backgroundColor = [UIColor blueColor];
    view.layer.borderWidth = 5.f;
    view.layer.borderColor = [UIColor redColor].CGColor;
    
    [self.view addSubview:view];
}
复制代码


Drop Shadows

iOS当中, 还有一个特性, 叫做Drop Shadows(阴影), 其实这个也不算是iOS特有的, 毕竟在Mac OS里早就已经有这个特性了. 阴影往往是起暗示作用, 比如说你现在正在显示的窗口, 周边就会带上阴影, 或者是强调里面的某个图层的优先级, 大多数时候只是用来装饰罢了. 设置阴影的时候也是很简单, 直接给shadowOpacity属性设置一个大于0.0的值就好了, shadowOpacity这个属性可设置的值是在0.0~1.0之间, 默认是0.f, 最大值是1.f, 如果直接设置为1.f, 那么将会显示一个轻微模糊的黑色阴影在图层的上方, 另外, 你可以通过CALayer所提供的shadowColor, shadowOffset, shadowRadius对阴影进行一些额外的操作, 这里就不一一介绍了, 各位童鞋们可以自行尝试一下~ 说到这里, 会有人问, 为什么阴影是在图层的上方呢? 其实在之前我们就有了解过Mac OS和iOS的一些东西, 基本上iOS的一些东西都是从Mac OS搬过来, 然后再改改的, 包括这个阴影也是如此, 所以你才会看到在iOS当中阴影是反过来的, 这里我们只需要设置一下shadowOffset就可以正常显示了. 直接看Demo吧:

- (void)layerDropShadows {
    
    UIView *view = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];
    
    view.backgroundColor = [UIColor redColor];
    view.layer.shadowOpacity = 1.0f;
    view.layer.shadowOffset = CGSizeMake(0, 3);
    view.layer.shadowRadius = 10.f;
    view.layer.shadowColor = [UIColor blueColor].CGColor;
    
    [self.view addSubview:view];
}
复制代码

Shadow Clipping

这里还有一个比较好玩的东西, 就是阴影剪切, 我们都知道CALayer超出了UIView的范围, 如果要剪切掉的话, 只能用masksToBounds这个属性去剪切. 但这样子会带来另一个的问题, 因为一旦使用masksToBounds这个属性, 剪切掉的就不只是多出来的部分, 会连阴影部分都一起剪切完, 为了防止阴影也跟着被剪切掉, 我们需要使用一个比较笨的方法, 就是再创建多一个CALayer, 让它去创建阴影部分, 另一个CALayer去剪切内容就可以了. 我们还是直接看Demo吧:

- (void)layerShadowClipping {
    
    UIView *contentView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
    UIView *blueView = [[UIView alloc] initWithFrame:CGRectMake(50, 50, 100, 100)];
    UIView *shadowView = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];
    
    contentView.backgroundColor = [UIColor grayColor];
    blueView.backgroundColor = [UIColor blueColor];
    
    contentView.layer.masksToBounds = YES;
    
    shadowView.layer.shadowOpacity = 1.0f;
    shadowView.layer.shadowOffset = CGSizeMake(0, 0.5f);
    shadowView.layer.shadowColor = [UIColor redColor].CGColor;

    [contentView addSubview:blueView];
    [shadowView addSubview:contentView];
    
    [self.view addSubview:shadowView];
}
复制代码

shadowPath

我们都知道其实阴影是没有形态的, 它是根据图层的形状来进行显示, 如果在一个视图当中有很多子图层, 然后要一个一个的去计算阴影的形状, 那是非常耗性能的, 但如果在开发之前你就已经知道阴影的形状, 那么你就可以提前设置好, 这样子就可以优化性能了, 而这个属性就是shadowPath, shadowPathCGPathRef类型, 可以说是一个指向CGPath的指针, 而CGPath是一个Core Graphics对象, 可以用来任意描绘一个矢量图形, 我们也可以使用它来描绘阴影, 大大的提升性能. 我们直接来看Demo吧:

- (void)layerShadowPath {
    
    UIView *shadowViewOne = [[UIView alloc] initWithFrame:CGRectMake(50, 50, 100, 100)];
    UIView *shadowViewTwo = [[UIView alloc] initWithFrame:CGRectMake(50, 250, 100, 100)];

    shadowViewOne.layer.shadowOpacity = 0.5f;
    shadowViewTwo.layer.shadowOpacity = 0.5f;
    
    CGMutablePathRef squarePath = CGPathCreateMutable();
    CGPathAddRect(squarePath, NULL, shadowViewOne.bounds);
    shadowViewOne.layer.shadowPath = squarePath;
    
    CGPathRelease(squarePath);
    
    CGMutablePathRef circlePath = CGPathCreateMutable();
    CGPathAddEllipseInRect(circlePath, NULL, shadowViewTwo.bounds);
    shadowViewTwo.layer.shadowPath = circlePath;
    
    CGPathRelease(circlePath);

    [self.view addSubview:shadowViewOne];
    [self.view addSubview:shadowViewTwo];
}
复制代码


Layer Masking

我们都知道如果我们要剪切超出范围的图层可以使用masksToBounds, 通过conrnerRadius可以设置图层为圆角, 但如果你需要设置一个不规整的图层时, 用上面的两个属性是没法实现的. 虽然我们可以使用一个32位且带有alpha通道的png图片可以实现, 但这个方法也是有局限的, 不能以动态编码的形式生成蒙版, 也不能让子图层或者子视图也裁剪成同样的形状. 苹果为了解决这个为题, 在CALayer当中提供了一个CGLayer类型, 名叫mask的属性, 有着和其他CALayer一样的布局属性, 它就像是一个子图层, 相对于父图层来进行布局的, 但它又区别于子图层, 它是定义父图层可见部分的. 虽然mask里也有Color这个属性, 但它并没啥用, 真正重要的是图层的轮廓, 它就像是一台切割机一样, 实心图层部分会被保留, 其他的就会作为垃圾一样被抛弃. 我们直接来看Demo吧:

- (void)layerMasking {
    
    UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];
    
    imageView.image = [UIImage imageNamed:@"arrow"];
    
    CALayer *maskLayer = [CALayer layer];
    UIImage *maskImage = [UIImage imageNamed:@"star"];
    
    maskLayer.frame = imageView.bounds;
    maskLayer.contents = (__bridge id _Nullable)(maskImage.CGImage);
    
    imageView.layer.mask = maskLayer;
    
    [self.view addSubview:imageView];
}
复制代码


Scaling Filters

这里我们提及一个点, 是关于图片显示的一个问题, 我们都知道如果要让一个CALayer显示一张图片是直接给它的contents直接设置内容, 但是呢, 这个图片是否正确的显示, 画质如何, 我们都无从而知. 为了解决这个问题,CALayer分别提供了minificationFiltermagnificationFilter属性, 它们都是NSString类型, 说到这里, 有人会疑问, 直接显示不就是最好的么, 为啥要那么麻烦, 其实不一定的, 原因的话, 大概有三点吧:

  • 能够显示最好的画质, 指的是像素没有被压缩也没有被拉伸.
  • 可以节省资源, 比如神马内存, 存储之类的.
  • 可以优化性能, 减轻CPU的压力.

打个比方, 比如我们开发的时候, 有个头像省略图, 这个时候呢, 你说是用全图比较好, 还是压缩过的比较好? 答案肯定是压缩过的比较好, 因为可以省资源. 顺便说说, 这两个属性可设置的值分别是:

  • kCAFilterLinear(默认值)
  • kCAFilterNearest
  • kCAFilterTrilinear 说那么多, 直接来看Demo吧:
- (void)layerScalingFilters {
    
    UIImageView *imageViewOne = [[UIImageView alloc] initWithFrame:CGRectMake(0, 100, self.view.bounds.size.width / 2, 200)];
    UIImageView *imageViewTwo = [[UIImageView alloc] initWithFrame:CGRectMake(self.view.bounds.size.width / 2, 100, self.view.bounds.size.width / 2, 200)];
    UIImageView *imageViewThree = [[UIImageView alloc] initWithFrame:CGRectMake(0, 300, self.view.bounds.size.width / 2, 200)];
    
    imageViewOne.image = [UIImage imageNamed:@"expression"];
    imageViewTwo.image = [UIImage imageNamed:@"expression"];
    imageViewThree.image = [UIImage imageNamed:@"expression"];

    imageViewOne.layer.magnificationFilter = kCAFilterNearest;
    imageViewTwo.layer.magnificationFilter = kCAFilterLinear;
    imageViewThree.layer.magnificationFilter = kCAFilterTrilinear;

    imageViewOne.layer.minificationFilter = kCAFilterNearest;
    imageViewTwo.layer.minificationFilter = kCAFilterLinear;
    imageViewThree.layer.minificationFilter = kCAFilterTrilinear;
    
    [self.view addSubview:imageViewOne];
    [self.view addSubview:imageViewTwo];
    [self.view addSubview:imageViewThree];
}
复制代码

额...这个有些尴尬, 看起来的效果区别不太大...凑合着看吧..


Group Opacity

最后, 我们来说说Group Opacity这个东西, 在iOS 7之前, 如果你在一个Button上添加一个视图, 在alpha设置为50%的情况下会出现图层有分割, 需要设置shouldRasterizerasterizationScale两个属性才能使得在半透明的情况下看起来是一体, 但是这个问题在iOS 7之后就已经解决了, 现在shouldRasterize属性更多的是用来解决UITableView里的CALayer圆角显示图层的处理, 这里就不做多的解释了, 有兴趣的童鞋们可以去谷歌搜搜shouldRasterize的用法, 都是挺简单的~


总结

总结一下, 这一章我们了解更多的是CALayer肉眼上能看到的东西, 比如:

  • Rounded Corners: 在这个知识点里, 我们了解了CALayer圆角的处理.
  • Layer Borders: 在这个知识点里, 我们了解了CALayer边框的处理, 包括边框的厚度, 以及颜色等
  • Drop Shadows: 在这个知识点里, 我们知道了CALayer阴影的处理.
  • Layer Masking: 在这个知识点里, 我们了解了CALayer可以作为一个mask去使用, 并且可以显示不同不规则的形状.
  • Scaling Filters: 在这个知识点里, 我们知道CALayer可以更改显示的画质, 优化显示的资源等等.
  • Group Opacity: 在这个知识点里, 虽然在iOS 7之后已经解决了这个问题, 但我们也可以把shouldRasterize属性运用在其他的地方.

工程地址

项目地址: https://github.com/CainRun/CoreAnimation


最后

码字很费脑, 看官赏点饭钱可好