最近有一个需求是要显示分数和星星图标,如果是4.8分就显示4个星星+80%填充的星星。
参考某团
一开始想的是通过UIBezierPath+CAShapeLayer手动绘制
然后上网找了一大堆五角星的坐标系算法,最后发现要么算的不对,要么太过于复杂,整个过程还是很麻烦的,而且一旦设计要的五角星图标还有各种弧线圆角,那算起来就更是一个无底洞了。所以这个方案直接pass。
图层蒙版
最近刚看完了《Core Animation》这本书(PS:讲的非常好,推荐大家一读),了解到CALayer有一个contents属性。
CALayer 有一个属性叫做 contents ,这个属性的类型被定义为id,意味着它可以 是任何类型的对象。在这种情况下,你可以给 contents属性赋任何值,你的app 仍然能够编译通过。但是,在实践中,如果你给 contents赋的不是CGImage, 那么你得到的图层将是空白的。
contents 这个奇怪的表现是由Mac OS的历史原因造成的。它之所以被定义为id 类型,是因为在Mac OS系统上,这个属性对CGImage和NSImage类型的值都起作 用。如果你试图在iOS平台上将UIImage的值赋给它,只能得到一个空白的图层。 一些初识Core Animation的iOS开发者可能会对这个感到困惑。
头疼的不仅仅是我们刚才提到的这个问题。事实上,你真正要赋值的类型应该是 CGImageRef,它是一个指向CGImage结构的指针。UIImage有一个CGImage属 性,它返回一个”CGImageRef”,如果你想把这个值直接赋值给CALayer的 contents ,那你将会得到一个编译错误。因为CGImageRef并不是一个真正的 Cocoa对象,而是一个Core Foundation类型。
尽管Core Foundation类型跟Cocoa对象在运行时貌似很像(被称作toll-free bridging),他们并不是类型兼容的,不过你可以通过bridged关键字转换。如果要 给图层的寄宿图赋值,你可以按照以下这个方法:
layer.contents = (__bridge id)image.CGImage;
同时CALayer有一个mask属性,
CALayer有一个属性叫做 mask 可以解决这个问题。这个属性本身就是个 CALayer类型,有和其他图层一样的绘制和布局属性。它类似于一个子图层,相对 于父图层(即拥有该属性的图层)布局,但是它却不是一个普通的子图层。不同于 那些绘制在父图层中的子图层, mask 图层定义了父图层的部分可见区域。
mask 图层的 Color 属性是无关紧要的,真正重要的是图层的轮廓。 mask 属 性就像是一个饼干切割机, mask 图层实心的部分会被保留下来,其他的则会被抛 弃。
如果 mask 图层比父图层要小,只有在 mask 图层里面的内容才是它关心的, 除此以外的一切都会被隐藏起来。
最终方案
那么,我们是不是就可以这样:我通过contents属性读取一张星星的图片赋值给一个layer图层,这个图层的绘制区域就会是整个图片区域,然后把这个图层作为mask赋值给我们的星星视图(就叫StarView)。那么StarView的可见区域只会是星星区域,其它部分都会被抛弃。
UIImage *starImage = [UIImage imageNamed:@"star"];
CALayer *maskLayer = [CALayer layer];
maskLayer.frame = CGRectMake(0, 0, w, h);
maskLayer.contents = (__bridge id)starImage.CGImage;
self.layer.mask = maskLayer;
这样我们的星星图案就出来了,这时候我在加一个view或者layer作为填充色覆盖
CALayer *fillLayer = [[CALayer alloc] init];
fillLayer.frame = CGRectMake(-w + w*0.7, 0, w, h);
fillLayer.backgroundColor = [UIColor orangeColor].CGColor;
[self.layer addSublayer:fillLayer];
这样,我们就实现了按照小数点多少来填充星星的效果了!
最后
强烈推荐没看过《Core Animation》这本书的同学看一遍,里面讲的很多知识点都是非常实用的。相信大家也都会收获良多。