iOS CoreAnimation(4)-视觉效果

来源:互联网 发布:手机怎么注册淘宝网 编辑:程序博客网 时间:2024/05/22 00:24

一、圆角

CALayer有一个属性叫做conrnerRadius的属性控制着圆层角的曲率。它是一个浮点数,默认值为0。默认情况下这个曲率值只影响背景颜色而不影响背景图片或者子图层。如果把masksToBounds设置为YES的话,图层里面的所有东西都会被截取。
这里写图片描述

二、图层边框

CALayer有另外两个非常有用的属性就是borderWidth和borderColor。二者共同定义了图层边的绘制样式。这条线沿着图层的bounds绘制,同时也包含图层的角。

borderWidth默认为0,borderColor默认为黑色。borderColor是CGColorRef类型,而不是UIColor,所以它不是Cocoa的内置对象,CGColorRef属性即便是强引用也只能通过assign关键字来声明。边框是绘制在图层边界里面的,而且在所有的内容之前,也在子图层之前。
这里写图片描述

仔细观察会发现边框并不会把寄宿图或子图层的形状计算进来,
这里写图片描述

边框跟随图层的边界变化的,而不是图层里面的内容。

三、阴影

图层的另一个特性就是阴影。给shadowOpacity属性一个大于默认值(0)的值,阴影就可以显示在任意图层之下。shadowOpacity是一个必须在0.0(不可见)和1.0(完全不透明)之间的浮点数。如果设置为1.0,就会显示一个有轻微模糊的黑色阴影稍微再图层之上。如果要改动阴影的表现,可以使用CALayer的另外三个属性:

  • shadowColor:控制阴影的颜色,类型是CGColorRef。
  • shadowOffset:控制阴影的方向和距离,是CGSize类型。宽度控制这阴影横向的位移,高度控制着纵向的位移。shadowOffset的默认值是 {0, -3},意即阴影相对于Y轴有3个点的向上位移。
  • shadowRadius:控制着阴影的模糊度,当它的值是0的时候,阴影就和视图一样有一个非常确定的边界线。当值越来越大的时候,边界线看上去就会越来越模糊和自然。苹果自家的应用设计更偏向于自然的阴影,所以一个非零值再合适不过了。

1. 阴影裁剪

和图层边框不同,图层的阴影继承自内容的外形,而不是根据边界和角半径来确定。为了计算出阴影的形状,Core Animation会将寄宿图(包括子视图,如果有的话)考虑在内,然后通过这些来完美搭配图层形状从而创建一个阴影(见图)。
这里写图片描述

当阴影和裁剪扯上关系的时候就有一个头疼的限制:阴影通常就是在Layer的边界之外,如果你开启了masksToBounds属性,所有从图层中突出来的内容都会被裁剪掉,包括阴影。

2. shadowPath属性

我们已经知道图层阴影并不总是方的,而是从图层内容的形状继承而来。这看上去不错,但是实时计算阴影也是一个非常消耗资源的,尤其是图层有多个子图层,每个图层还有一个有透明效果的寄宿图的时候。

如果你事先知道你的阴影形状会是什么样子的,你可以通过指定一个shadowPath来提高性能。shadowPath是一个CGPathRef类型(一个指向CGPath的指针)。CGPath是一个Core Graphics对象,用来指定任意的一个矢量图形。我们可以通过这个属性单独于图层形状之外指定阴影的形状。

这里写图片描述

@interface ViewController ()@property (nonatomic, weak) IBOutlet UIView *layerView1;@property (nonatomic, weak) IBOutlet UIView *layerView2;@end@implementation ViewController- (void)viewDidLoad{  [super viewDidLoad];  //enable layer shadows  self.layerView1.layer.shadowOpacity = 0.5f;  self.layerView2.layer.shadowOpacity = 0.5f;  //create a square shadow  CGMutablePathRef squarePath = CGPathCreateMutable();  CGPathAddRect(squarePath, NULL, self.layerView1.bounds);  self.layerView1.layer.shadowPath = squarePath; CGPathRelease(squarePath);  //create a circular shadow  CGMutablePathRef circlePath = CGPathCreateMutable();  CGPathAddEllipseInRect(circlePath, NULL, self.layerView2.bounds);  self.layerView2.layer.shadowPath = circlePath; CGPathRelease(circlePath);}@end

如果是一个矩形或者是圆,用CGPath会相当简单明了。但是如果是更加复杂一点的图形,UIBezierPath类会更合适,它是一个由UIKit提供的在CGPath基础上的Objective-C包装类。

四、图层蒙版

CALayer的Mask属性可以实现蒙版的操作。
CALayer有一个属性叫做mask可以解决这个问题。这个属性本身就是个CALayer类型,有和其他图层一样的绘制和布局属性。它类似于一个子图层,相对于父图层(即拥有该属性的图层)布局,但是它却不是一个普通的子图层。不同于那些绘制在父图层中的子图层,mask图层定义了父图层的部分可见区域。

mask图层的Color属性是无关紧要的,真正重要的是图层的轮廓。mask属性就像是一个饼干切割机,mask图层实心的部分会被保留下来,其他的则会被抛弃。(如图)

如果mask图层比父图层要小,只有在mask图层里面的内容才是它关心的,除此以外的一切都会被隐藏起来。

这里写图片描述

代码如下:

@interface ViewController ()@property (nonatomic, weak) IBOutlet UIImageView *imageView;@end@implementation ViewController- (void)viewDidLoad{  [super viewDidLoad];  //create mask layer  CALayer *maskLayer = [CALayer layer];  maskLayer.frame = self.imageView.bounds;  UIImage *maskImage = [UIImage imageNamed:@"Cone.png"];  maskLayer.contents = (__bridge id)maskImage.CGImage;  //apply mask to image layer  self.imageView.layer.mask = maskLayer;}@end

五、拉伸过滤

两个属性minificationFilter和magnificationFilter属性。总的来讲,当我们视图显示一个图片的时候都应该正确的显示这个图片。原因:

  • 能够显示最好的画质,像素既没有被压缩也没有被拉伸。
  • 能更好的使用内存,因为这就是所有你要存储的东西。
  • 最好的性能表现,CPU不需要为此额外的计算。

不过有时候,显示一个非真实大小的图片确实是我们需要的效果。比如说一个头像或是图片的缩略图,再比如说一个可以被拖拽和伸缩的大图。这些情况下,为同一图片的不同大小存储不同的图片显得又不切实际。

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

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

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

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

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

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

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

对于纯色系列的图片,首要选择使用kCAFilterNearest。

六、组透明

UIView有一个叫做alpha的属性来确定视图的透明度。CALayer有一个等同的属性叫做opacity,这两个属性都是影响子层级的。也就是说,如果你给一个图层设置了opacity属性,那它的子图层都会受此影响。

iOS常见的做法是把一个空间的alpha值设置为0.5(50%)以使其看上去呈现为不可用状态。对于独立的视图来说还不错,但是当一个控件有子视图的时候就有点奇怪了,如图展示了一个内嵌了UILabel的自定义UIButton;左边是一个不透明的按钮,右边是50%透明度的相同按钮。我们可以注意到,里面的标签的轮廓跟按钮的背景很不搭调。

这里写图片描述

这是由透明度的混合叠加造成的,当你显示一个50%透明度的图层时,图层的每个像素都会一般显示自己的颜色,另一半显示图层下面的颜色。这是正常的透明度的表现。但是如果图层包含一个同样显示50%透明的子图层时,你所看到的视图,50%来自子视图,25%来了图层本身的颜色,另外的25%则来自背景色。

在我们的示例中,按钮和表情都是白色背景。虽然他们都是50%的可见度,但是合起来的可见度是75%,所以标签所在的区域看上去就没有周围的部分那么透明。所以看上去子视图就高亮了,使得这个显示效果都糟透了。

可以设置CALayer的一个叫做shouldRasterize属性来实现组透明的效果,如果它被设置为YES,在应用透明度之前,图层及其子图层都会被整合成一个整体的图片,这样就没有透明度混合的问题了。

为了启用shouldRasterize属性,我们设置了图层的rasterizationScale属性。默认情况下,所有图层拉伸都是1.0, 所以如果你使用了shouldRasterize属性,你就要确保你设置了rasterizationScale属性去匹配屏幕,以防止出现Retina屏幕像素化的问题。

当shouldRasterize和UIViewGroupOpacity一起的时候,性能问题就出现了。

0 0