设置图层对象
图层对象是你使用CoreAnimation(以下皆称CA)所进行交互的中心。图层管理你app中的视图内容,并且给这些内容提供可修改的样式和视觉外貌。因为OS X和IOS的不同,要使用CA必须开启该功能,以下一小节将了如何在OS X中开启改效果,所以就略过。
改变一个与视图所关联的图层对象
图层所关联的视图默认创建一个CALayer实例,并且大多数情况下,你并不需要一个不同的图层对象。然而,CA提供了不同的图层类,每一个图层类都提供特定用途。选择某一个不同的图层类可能会提高性能,或者以一种简单的方式来支持某一种视图内容行为。比如CATiledLayer类为展示大图片提供了更高的效率。
改变UIView的Layer Class
你可以重写视图的layerClass方法,以使得该方法返回某一特定的图层类。大多数IOS视图创建CALayer对象,并使用该对象作为后备存储(backing store)。然而对于大多数你自己的视图,这种默认的选择也是很不错的,所以除非必要,不要改变。但是你可能发现某一类型的图层类型更适合在某些情况下。比如,你可能会想要改变图层类型在以下情况的时:
1.你的视图使用到OpenGL 来绘图,这种情况下 你就要用到CAEALLayer对象。
2.某一类的图层类提供了更好的性能。
3.你想要CA图层中的某些特性。比如粒子发光特效。
改变视图的的图层类十分简单,见以下例子2-1。你唯一需要做的就是重写layerClass方法并且将返回的图层类对象改变成你想要返回的。在显示display画面之前,视图会调用layerClass,并且使用返回的视图对象创建新的图层。一旦完成创建,图层就不能再改变了。
+ (Class) layerClass {
return [CAMetalLayer class];
}
想要了解更多图层类的更多特性见Different Layer Classes Provide Specialized Behaviors.
不同的图层类提供各种特性
CA定义了许多标准图层类,每一个都有特定的使用环境。CALayer是这些图层类的基类。它定义了这些子图层类所必需支持的,并且是视图类所默认使用的。详细见下表2-1
Class 作用
CAEmitterLayer 用来实现基于CA的粒子发光效果,该图层对象控制粒子的产生。
CAGradientLayer用来绘制有色彩梯度的图层
CAMetalLayer 用来建立和。。。这个确实不太明白
CAEAGLLAyer 主要是开发OpenGL所用的图层
CAReplicatorLayer 当你想要自动复制多个子图层的使用
CAScrollLayer 用来管理大片科幻懂的区域并且有多个子图层
CAShapeLayer 用来绘制基于路径的图形的图层
CATextLayer 主要是用来渲染属性字
CATiledLayer 主要用来管理大的图片,该图片可以奋争若干个小的贴瓷,并且支持缩放其中的内容
CATransformLayer 主要用来渲染3D效果
为图层提供内容
图层是管理你app的内容的数据对象。一个图层内容由包含视觉数据的位图组成。你可以以一下三种方式来提供内容。
1.使用图片作为图层的内容,这种方法能为那些静态的或者极少改变的内容提供良好效果
2.使用委托方式(delegate),让委托来绘制图层内容。这种方式对于那些周期改变内容的视图具有很好的效果。
3.直接定义一个图层的子类,然后自己给图层提供内容。如果你想改变基本的图层绘制方式,或者自定义一个图层,那么就使用该种方式。
你在使用这些方式的时候唯一需要担心的在自己创建图层的时候。如果你的app只包含了图层支持的视图,那么你大可放心的使用。图层支持的视图将以最效率的方式为图层提供其内容。
使用图片作为图层的内容
因为图层仅仅是一个管理位图图像的容器,你可以直接将图片传递给图层的contents属性。给图层的属性赋值图片相当简单,并且能够让你准确的指定你想如何呈现图片。图层使用图片对象的时候并不会创建图片的复制copy。这一机制当你在多处使用该图片的时候并不会产生额外的内存开销。
你传递给图层的图片类型必须是CGImageRef类型。记得一定要使用合适分辨率的图片。
使用委托的方式来提供图层内容
如果你的图层内容经常的改变,你可以使用一个委托对象来提供并更新内容。在展现的时候,图层将调用委托的方法来提供必要的内容。
如果你的委托实现了displayLayer方法,那么委托就要负责创建位图并且将位图赋值给图层的contents属性。
如果你的委托实现了drawLayer:inContext方法,CA创建位图,然后讲图像上下位context画进位图。然后调用你的委托方法来填充位图。你的委托方法需要的就是将必要的内容画进去。
委托对象必须实现displayLayer或者drawLayer:inContext两个方法中的任意一个。如果两者都实现了,那只会调用displayLayer方法。
当你的app更倾向于加载或者创建它所想要展示的内容的情况下,重写displayLayer方法将会是最适合的。LIst2-3展示一个例子来实现displayLayer委托方法。在该例中,委托使用了帮助者对象来加载和展示它所需要的内容。委托方法选择哪张图片需要被展示,基于其内部状态。就是例子中displayYesImage属性。
- (void)displayLayer:(CALayer *)theLayer {
// Check the value of some state property
if (self.displayYesImage) {
// Display the Yes image
theLayer.contents = [someHelperObject loadStateYesImage];
}
else {
// Display the No image
theLayer.contents = [someHelperObject loadStateNoImage];
}
}
如果你不想预渲染图片或者预先创建位图,你可以使用drawLayer:inContext方来动态的加载。如2-4所显示的例子。在该方法中委托绘制了一条曲线。
- (void)drawLayer:(CALayer *)theLayer inContext:(CGContextRef)theContext {
CGMutablePathRef thePath = CGPathCreateMutable();
CGPathMoveToPoint(thePath,NULL,15.0f,15.f);
CGPathAddCurveToPoint(thePath,
NULL,
15.f,250.0f,
295.0f,250.0f,
295.0f,15.0f);
CGContextBeginPath(theContext);
CGContextAddPath(theContext, thePath);
CGContextSetLineWidth(theContext, 5);
CGContextStrokePath(theContext);
// Release the path
CFRelease(thePath);
}
对于定制内容的基于图层的视图,你应重载视图的以上两种方法。基于图层的视图会使自身成为委托,然后实现其两种方法,你并不需要改变其任何配置。作为替代的,你应该重写drawRect方法来绘制你的内容。
通过图层子类提供图层所需的内容
如果你实现了一个定制的图层类,你可以重写你的图层类方法来绘制。通常来说,通过图层来产生内容并不是那么的常见,但是图层确实可以这么做来管理内容。比如,CATiledLayer类管理一个由多块小图所组成的大图片,该图层会分别渲染。只要提供必要的信息,该视图类就会直接绘制其内容。
当使用到子类的时候,你需要遵循以下原则来绘制你的内容:
1.重写图层的display方法,并合理的设置图层属性。
2.重写图层的drawInContext方法,并且在该方法内提供必要的内容
使用以上两种方法决定于你绘制的过程。display方法是你更新图层内容的主入口,所以重写该方法会让你完全的控制绘图过程。重写display方法也意味这你需要创建CGImageRef并赋值给contents属性。如果你仅仅只是想绘制内容,并不想完全的掌控绘图的细节。重写drawInContext方法,图层会为你创建后备存储的。
调整图层内容
当你给你的图层contents属性添加一张图片作为内容时,图层的contentsGravity属性决定了图片时如何适配在当前边界的。默认情况下,图片过大或者过小,图层酒会调整图片成图层的可用大小。如果图层的边界比例与你的图片的比例不一致,这样图片将会失真。所以通过更改contentsGravity属性可以让你的图片适配当前图层。
你可以给contentsGravity属性设置的值可以分为两大类:
1.基于位置的gravity常量能够让你的图片紧贴某一边,或者某一角 而不会调整图片的来适应图层。
2.基于缩放的gravity 常量让你的的图片缩放,保持某一比例或者不保持该比例。
图2-1 展示了基于位置的gravity设置如何影响你的图片。除了kCAGravityCenter常量,其他的常量都会让你的图片紧贴某一边或者某一角。kCAGravityCenter常量,让你的图片居中。这些常量都不会缩放你的图片,所以图片总是以原本的分辨率进行渲染。如果图片大于图层的边界(bounds),那么就进行裁剪。如果图片小于边界,那么如果设置了图层的背景眼,除了图片部分以外的部分将会呈现图层的背景颜色。
图2-2展示了基于缩放的gravity 常量如何影响你的图片。这些常量缩放你的图片如果图片并不能准确的显示在图层中。这些常量的区别在于他们如何去处理图片的原来长宽比例。一些常量会保持原有的比例,而一些并不会。默认情况下,图层的contentsGravity属性被设置成 kCAGravityResize,这也是唯一的常量模式并不会保持图片原有比例。
使用高分辨率的图片
图层并不知道如何处理图片的分辨率。图层仅仅只是存储一个指针,指针指向位图,然后尽可能的以最好的方式呈现像素。如果你将一张图片赋值给contents属性,你就必须通过设置contentsScale属性告诉CA关于图像的分辨率改如何调整,该属性的默认值是1,大多数情况下这个值能合理的处理图片分辨率,但是当用到Retina屏上,就需要设置成2.0。
改变contentsScale属性只有当你直接将位图赋值给图层的时候才需要。在UIKit和AppKit中,基于图层的视图会自动的设置该属性以匹配屏幕。
调整图层的视觉样式和外观
图层对象的内置装饰属性如boder边框,backgroundColor背景颜色,这些可以让你来作为图层主要内容的装饰。想知道这些视觉装饰属性如何影响图层的见Layer Style Property Animations.
图层都有自己的背景色和边框
图层可以显示背景色,并且可以添加上边框。背景色在图层图片内容之下渲染,而边框则是出于最顶层。就像图2-3.如果图层包含子图层,那么子图层也在边框之下。因为背景色是在你图片之下的,所以背景色会透过你图片透明的部分。
如2-5所示,这些代码设置图层的背景色和边框。这些属性都是可以设置动画效果的。
myLayer.backgroundColor = [NSColor greenColor].CGColor;
myLayer.borderColor = [NSColor blackColor].CGColor;
myLayer.borderWidth = 3.0;
如果你的图层背景色是不透明的,记得将图层的opaque属性设置为YES.这样可以提高性能,比如两两个图层部分相互重叠时。切记如果你的图层边角要设置成圆弧的时候 就不要将其设置为不透明YES。
图层支持圆角
你可以改变边角的弧度半径为你的图层添加圆角边框效果。边角弧度半径是一种将原来的直角遮蔽的装饰效果。如图2-4所示,应为涉及到应用透明遮盖问题,边角弧度半径并不会影响图层中内容,除非设置了maskToBounds=YES.但是边角弧度半径总是会影响图片的背景颜色和边框的样子。
如果要让图层能变成圆角,那么就给cornerRadius属性赋值即可。该属性会应用于四个边角,并且优先展示。
图层内置的阴影
CALayer类包含了多种属性,让你来配置阴影效果。阴影效果通过让你感觉内容就像是浮出底层能增加图层的层次感。阴影效果在你app中合理的使用能够为你的app加分增彩。在图层中,你可以控制阴影的颜色,透明度和形状。
图层阴影的opaque值默认是0,也就是不显示阴影。改变该值,会让CA绘制出阴影。因为阴影默认是由图层来调整位置的,所以,你需要指定阴影的偏移offset来调整阴影的位置。特别需要注意的,该偏移是使用图层的内置坐标系。如图2-5所示
给图层添加自定义属性
CAAnimation和CALayer类通过key—value准则来支持自定义属性。你可以利用该特性来添加或得到值通过自定的key。你甚至可以给某一行为添加自定义的属性,这样当你改变该属性的时候,相应的动画就会执行。
更多信息见Key-Value Coding Compliant Container Classes.