Core Animation 阅读随笔 第四章 - 视觉效果

16/04/23 第四章视觉效果

borderWidth

  1. borderWidth是绘制在frame之内的内边框.
  2. borderWidth的绘制只是沿着图层的边框绘制, 如果layer上的图片(寄宿图)超出layer并显示, borderWidth依然只沿着layer的边界绘制, 而不会包括超出边界的寄宿图, 即:borderWidth是跟随图层边界变化,而非图层里面的内容;

阴影

  1. 阴影和上述边框不同, 阴影是跟随图层内容的, 而非图层边框.
  2. 当即需要阴影又需要裁剪时, 裁剪会把阴影也裁剪掉, 可在图层外再包一个视图,设置外包视图的阴影, 裁剪内容视图;
  3. 默认的阴影效果是跟随图层内容的, 非常消耗资源, 可以通过shadowPath来提高性能.
  4. shadowPathCGPathRef类型,是指向CGPath的指针, CGPathCore Graphics对象,用来指定任意一个矢量图形,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
    
@interface ViewController ()

@property (nonatomic, weak) IBOutlet UIView *layerView1;
@property (nonatomic, weak) IBOutlet UIView *layerView2;
@end

@implementation ViewController

- (void)viewDidLoad
{
[super viewDidLoad];

//阴影模糊度
self.layerView1.layer.shadowOpacity = 0.5f;
self.layerView2.layer.shadowOpacity = 0.5f;

///创建长方形阴影
// 1. 创建绘制路径
CGMutablePathRef squarePath = CGPathCreateMutable();
// 2. 指定绘制长方形的范围
CGPathAddRect(squarePath, NULL, self.layerView1.bounds);
// 3. 设置路径
self.layerView1.layer.shadowPath = squarePath;
// 4. releaase
CGPathRelease(squarePath);

///创建椭圆阴影
// 1. 创建绘制路径
CGMutablePathRef circlePath = CGPathCreateMutable();
// 2. 指定绘制椭圆的范围
CGPathAddEllipseInRect(circlePath, NULL, self.layerView2.bounds);
// 3. 设置路径
self.layerView2.layer.shadowPath = circlePath;
// 4. releaase
CGPathRelease(circlePath);
}
@end

mask蒙版遮罩属性

  1. mask属性本身是一个CALayer,定义了父图层的部分可见区域;
  2. mask属性的color是无关紧要的, 本是就像一个模具(比如:小熊饼干模具,月饼等形状模具),如果mask比父视图小, 那么只会显示mask这个模具形状的内容. 比如你父视图是红色的矩形, 而mask是一个任意颜色的五角星, 那么最终显示结果是一个红色的五角星, 超出mask范围外的都会隐藏. 就像模具.
  3. mask蒙版图层不仅仅限于静态图,任何图层构成的都可以作为mask属性.意味着你可以通过代码或动画来实时生成;

拉伸过滤

最后我们再来谈谈minificationFilter和magnificationFilter属性。总得来讲,当我们视图显示一个图片的时候,都应该正确地显示这个图片(意即:以正确的比例和正确的1:1像素显示在屏幕上)。原因如下:

能够显示最好的画质,像素既没有被压缩也没有被拉伸。
能更好的使用内存,因为这就是所有你要存储的东西。
最好的性能表现,CPU不需要为此额外的计算。
不过有时候,显示一个非真实大小的图片确实是我们需要的效果。比如说一个头像或是图片的缩略图,再比如说一个可以被拖拽和伸缩的大图。这些情况下,为同一图片的不同大小存储不同的图片显得又不切实际。

当图片需要显示不同的大小的时候,有一种叫做拉伸过滤的算法就起到作用了。它作用于原图的像素上并根据需要生成新的像素显示在屏幕上。

事实上,重绘图片大小也没有一个统一的通用算法。这取决于需要拉伸的内容,放大或是缩小的需求等这些因素。CALayer为此提供了三种拉伸过滤方法,他们是:

kCAFilterLinear
kCAFilterNearest
kCAFilterTrilinear
minification(缩小图片)和magnification(放大图片)默认的过滤器都是kCAFilterLinear,这个过滤器采用双线性滤波算法,它在大多数情况下都表现良好。双线性滤波算法通过对多个像素取样最终生成新的值,得到一个平滑的表现不错的拉伸。但是当放大倍数比较大的时候图片就模糊不清了。

kCAFilterTrilinear和kCAFilterLinear非常相似,大部分情况下二者都看不出来有什么差别。但是,较双线性滤波算法而言,三线性滤波算法存储了多个大小情况下的图片(也叫多重贴图),并三维取样,同时结合大图和小图的存储进而�得到最后的结果。

这个方法的好处在于算法能够从一系列已经接近于最终大小的图片中得到想要的结果,也就是说不要对很多像素同步取样。这不仅提高了性能,也避免了小概率因舍入错误引起的取样失灵的问题.

对于大图来说,双线性滤波和三线性滤波表现得更出色

kCAFilterNearest是一种比较武断的方法。从名字不难看出,这个算法(也叫最近过滤)就是取样最近的单像素点而不管其他的颜色。这样做非常快,也不会使图片模糊。但是,最明显的效果就是,会使得压缩图片更糟,图片放大之后也显得块状或是马赛克严重。

对于没有斜线的小图来说,最近过滤算法要好很多

总的来说,对于比较小的图或者是差异特别明显,极少斜线的大图,最近过滤算法会保留这种差异明显的特质以呈现更好的结果。但是对于大多数的图尤其是有很多斜线或是曲线轮廓的图片来说,最近过滤算法会导致更差的结果。换句话说,线性过滤保留了形状,最近过滤则保留了像素的差异。

组透明

  1. UIViewalpha属性可设置透明度, CALayer与之对应的是opacity, 两个属性都会对子视图造成影响.
  2. 在自定义视图中, 对父视图进行透明度设置, 子视图的透明度会跟随父视图, 虽然如此,但最终显示结果却不尽人意, 这是因为最终显示结果由两个视图叠加而成, 也就是说,如果父视图透明度为50%, 子视图也为50%, 系统绘制图层时,会根据两者的透明度混合叠加. 最终重叠部分的透明度看起来像75%. 可以通过CALayershouldRasterize属性来实现组透明效果.
  3. shouldRasterize属性设置为YES会在透明度被应用之前将所有视图合成一张整体图片. 但当设置shouldRasterize属性为YES时,为了防止Retina屏幕像素化, 就需要设置rasterizationScale属性去匹配屏幕. 而一旦这两个属性同时设置时会出现性能问题.会在12章和15章讲解.