原创

IOS 关于 core animation 文档详细翻译

what is core animation?

core animation是objective-c类中图形渲染,投影和动画的一个集合。它提供了先进的合成效果。

我们想创建一些动态的动画,如果用interface是很困难的,但是core animation能够创建这些interface通过提供下面这些:

(1)高性能合成与容易掌握的编程模型

(2)创建复杂的user interface通过使用layer对象的继承

(3)一个轻量级的数据结构,你可以同时显示动画数百层

(4)抽象的动画界面允许动画在单独的线程中,从而独立与程序的run loop。一旦动画配置后并开始,core animation会承担全部的责任去运行它的帧速。

(5)提高应用性能,程序仅仅在它改变的时候才需要重绘内容,用于调整和布局服务层提供最小的应用程序交互。

(6)拥有一个灵活的布局管理模型,包括管理与layer的兄弟层的位置和大小。


使用core animation,开发者可以为他们的程序创建动态的用户界面,避免了使用底层的图形API,例如OPenGl等等。


core animation是oc的一个框架,它将一个高性能的合成引擎与一个简单的动画编程接口结合起来。


core animation类可以分为下面几个:

(1)提供显示layer的类

(2)动画和时间类

(3)布局和约束类

(4)使多个层变成几个原子更新的执行类

core animation包含的基础类的都在Quartz Core framework中,虽然附加的类层可以在其他的框架中定义。

下面是core animation类继承的层次图

1CC0ECB3-E888-4EBD-B41C-2FCAFF8B178C.jpg

层类(Layer Classes)

Layer Classes是core animation的基础。Layer Classes提供了一个抽象的概念,这个概念对于那些使用NSview和UIview的开发者来说是很熟悉的。基础层是由CAlayer类提供的,CAlayer是所有Core Animation层的父类。


同一个视图类的实例一样,一个CAlayer实例也有一个单独的superlayer和上面所有的子层(sublayers),它创建了一个有层次结构的层,我们称之为layer tree。layers的绘制就像views一样是从后向前绘制的,绘制的时候我们要指定其相对与他们的superlayer的集合形状,同时还需要创建一个局部的坐标系。layers润需一些更复杂的操作,例如rotate(旋转),skew(倾斜),scale(放缩),和project the layer content(层的投影)。


CAlayer在Application kit和Cocoa touch view类中是不同的,在Cocoa touch view类中不需要为了显示内容而去子类化CALayer。可以提供给CAlyer实例显示的内容有:

(1)直接设置层的content属性到一个core graphics图,或者通过delegation来设置

(2)提供一个代理直接绘制到Core Graphics image context(核心图形的上下文)

(3)设置任意数量的所有层共有的可视的风格属性。例如:backgroundColor(背景色),opacity(透明度)和masking(遮罩)。max os x应用通过使用core image filters来达到这种可视化的属性。

(4)子类化CAlayer,同时在更多的封装方式中完成上面的任意技术。


除Calayer类意外,Core animation还提供了额外的类来显示其他类型的内容。在mac os x和ios中这些类略有不同。


下面这些类可以同时在max os x和ios中使用:

(1)CAScrollLayer:它是CALayer的一个子类,用来显示layer的某一部分,一个CAScrollLayer对象的滚动区域是由其子层的布局来定义的。CAScrollLayer没有提供键盘或者鼠标事件,也没有提供明显的滚动条。

(2)CATextLayer:它是一个很方便就可以从string和attributed string创建layer的content的类。

(3)CATIledLayer:它允许在增量阶段显示大和复杂的图像(就是将图形进行分块显示,来减少内存)


mac os x提供下面这些额外的类:

(4)CAOpenGLLayer:它提供了OPenGL的渲染环境,你必须子类化这个类来提供content来使用OpenGl,这个content可以是静态的也可以是随着时间推移来更新的。

(5)QCCompositionLayer:(它由QUartz Composer framework提供)产生一个动画作为QUartz Composer composition的content。

(6)QTMovieLayer和QTCaptureLayer:(由QTKit framework提供)提供QuickTime电影和视频的playback。


ios增加下面的类:

(7)CAEAGLLayer:它提供了OPenGLES的渲染环境


CAlyer类也介绍了key-value coding compliant container class(键 - 值编码兼容的容器类)的概念。这个类可以存储任意值,可以使用key-value coding compliant的方法,而不必创建一个子类。CAlayer还延伸了NSKeyValueCoding的非正式协议,增加了对默认键值的支持和自动包装一个结构体类型(CGPOint,CGRect,CGSize,CGAffineTransform和CAtransformD),同时也提供了通过key path来获取这个结构体的数值。


CALayer也管理动画和与其相关的layer的actions。layers接收一些从layer tree中触发的insert和remove消息,修改被创建的layer的属性,或者指明开发者的需求。这些actions通常都会导致动画的产生。


动画和计时类(Animation and Timing Classes)

层的许多可视属性都是隐式的动画,通过简单的修改动画属性的值,层会自动的从当前的value到new value产生动画。例如:设置layer的hidden属性为YES将会触发该layer逐渐淡出的动画。大多数动画属性都有默认的动画,你可以很容易的自定义和替换他们。

层的动画属性也可以被设置成显式的动画。为了明确动画属性你要创建Core animation动画类的一个实例,同时指定所需要的可视效果。在layer中一个明确的动画并没有改变属性的值,而仅仅是以动画的形式来显示它。

core animation提供了可以动画类,这些类可以使layer中的内容进行动画或者用basci animation和key-frame animation来选择属性。所有的core animation的动画类都是从CAAnimation类抽象而来。CAAnimation遵守CAMediaTiming协议,该协议提供了简单duration(持续时间),speed(速度)和repeat count for an animation(动画重复的次数)。CAAnimation也遵守CAAction协议,这个协议为开始动画提供了一个标准的方法来响应一个layer的行为触发(action triggered).

动画类定义了一个timing(计时)功能来描述动画的每步作为一个简单的贝塞尔曲线。例如:一个线性计时功能会指定动画的每一步,即使是在整个运行期间,一个ease-out功能会引发动画随着它接近duration而慢下来。

core animation提供了下面这些额外的抽象和具体的类:

(1)CATranstion:提供了影响整个层内容过渡的效果,在动画期间它使层产生fade(渐变),push(推拉)以及reveals(揭示)的动画效果。这些过渡的效果可以通过你自己自定义的core image filters来扩展。

(2)CAAnimationGroup:允许动画对象的数组组合在一起,并同时运行。

(3)CAPropertyAnimation:它是CAAnimation的一个抽象子类,支持层在动画期间为层提供key path。

(4)CABasicAnimation:为层的属性提供了简单的插值。

(5)CAkeyframeAnimation:提供了关键帧动画的支持。你可以为层属性指定key path来使其产生动画,这个数组的值保存了动画每个阶段的值,同时还有key frame的次数和时间函数。在动画运行的时候,数组中的每个值就会被轮流进行插值使用。

这些类都core animation和cocoa animation代理使用。

Layout Manager classes(布局管理类)

Application kit视图类相对他们的superlauer提供了经典的“struts and springs”定位层模型。当layer支持这些模型的时候,在mac os x上的core animation也提供了更灵活的布局管理机制,这个机制允许开发者编写他们自己的布局管理。

core animation的CAConstraint类是一个布局管理类,该类用你指定好的约束条件来重排sublayers。每个约束条件都描述了与这个layer的兄弟层或者它的superlayer的相关的几何特性。

事务管理类(Transaction Management Classes)

每个层的动画属性的修改都是事务的一部分。CATransaction是来把多个动画操作分配到原子更新来显示。支持事务嵌套。


core animation支持两种类型的事务:隐式事务和显示事务。当一个layer的动画属性被一个没有活跃的事务和线程run-loop下一次迭代自动提交的线程所修改的时候,隐式事务会自动的被创建。当应用程序在layer修改之前发送一个CAtransaction类开始的消息的时候显示事务会发生,之后还要提交一个信息。


core aniamtion rending architecture

在core aniamtion和cocoa view之间有很大的相似之处,他们之间最大的概念上的分歧就是layer不直接渲染到屏幕上。

在MVC的设计模式下,NSView和UIView是视图对象,core animation层实际上是模型对象。他们封装了几何图形,时间和可视属性,同时提供显示的内容,但是实际上显示并不是layer的责任。


每一个可视的layer tree后面都有两个相应的tree:presentation tree和render tree。

图1:core animation 渲染结构

794CABBE-D005-4BE0-9FF3-7596E6ABEBA6.jpg

layer tree包含了每个layer的对象模型。当你为一个layer的属性设置一个的时候,他们的值就是你设置的。


The presentation tree包含了当前正在呈现给用户作为动画发生的值。例如:对一个layer的backgroundcolor设置一个新的值的时候,在layer tree中的值会马上改变。然而,在presentation tree的相应层的backgroundcolor的值随着要显示给用户的插值颜色会被更新。


当渲染一个layer的时候,the render-tree会使用presentation-tree的值。the render-tree的责任就是执行独立与程序活动的合成操作;渲染是在一个单独进程或者线程中,以便使其对应用程序的run loop影响最小。


当一个动画在执行的过程中,你可以查询相应的presentation layer的实例。如果你打算改变当前的动画并且从当前显示状态开始一个新的动画,这是非常有用的。

5A00F88D-CC4B-40C2-B246-67832854CE8E.jpg

position属性是一个CGPoint,用来指定layer与其superlayer的相对位置,同时表示它在superlayer的坐标系中。

bounds属性是一个CGRect,它提供了layer的size和origin。当你重载layer的绘制方法的时候bounds的origin被用来作为graphics context的origin。

layers有一个隐含的frame,这个frame是position,bounds,anchorPoint和transform属性的功能。可以适当的为layer设置新的position和bounds属性,但是frame本身并没有被存储。当你设置一个新的框架矩形的bounds origin是不受外界所干扰的时候,这时候bounds size会设置成frame的size。layer的position设置成正确location是与anchor point有关的。当你得到frame的属性值的时候,它会计算相对应的position,bounds和anchorPoint属性的值。


anchorPoint属性是一个CGPoint,它会在与位置坐标相关的layer的bounds内指定一个location。anchor point指定了bounds是如何相对于position属性的位置,同时为周围的转换点做服务。它主要变现在单位坐标系中,点(0.0,0.0)在layer的原点,点(1.0,1.0)在layer中与原点相对的角。对layer的父层(如果存在)做转换可以改变anchorPoint的方向,y轴取决于父坐标系。


当你指定layer的frame,position是与anchorPoint有关的。当你指定layer的position,bounds是与anchorPoint有关的。


ios小提示:下面的例子展示了在mac os x中的layer,默认坐标系原点是左下角。on ios,默认的是左上角,正值方向是向下和向右。


图2 3个锚点(mac os x)

anchorPoint默认值是(0.5,0.5),与layer的bounds的中心相关。(图中的A)B点显示的anchorPoint是(0.0,0.5).最后C点(1.0,0.0)引起layer的position设置在frame的右下角。

图片:

D17A7431-3A2F-4923-A5F7-8D369D7E65AB.gif

 frame,bounds,position和anchorPoint在图3中显示

8954C66A-1749-4AF5-AF8D-F80BE8D86F26.jpg

上面的例子中anchorPoint设置为默认值(0.5,0.5),与layer的center有关。layer的position设置为(100.0,100.0),bounds设置为(0.0,0.0,120.0,80.0).这样frame属性经计算得出为(40.0,60.0,120.0,80.0).

如果你创建了一个新layer,仅仅设置了layer的frame属性为(40.0,60.0,120.0,80.0),position自动就为(100.0,100.0),,bounds属性就为(0.0,0.0,120.0,80.0)。

图4显示的layer与图3中的layer有相同的frame rectangle。然而,在这种情况layer下的anchorPoint是(0.0,0.0),即layer的左下角。

2372E755-539C-443E-9D25-E01467D8CCF4.jpg

设置frame为(40.0,60.0,120.0,80.0),bounds的值也是一样,但是这时候position的值已经改变了。


不同cocoa views的几何图形的另外一个方面是你可以指定的半径用作layer的角落的弧形。当绘制layer中的内容,裁剪子层,或者边界和阴影的时候,cornerRadius属性可以指定一个半径供layer使用。


zPosition属性指定了layer在z轴中的位置。zPosition是用于设置相对于其兄弟层的可视的位置。它不能用来指定层之间的顺序,也不是重新排布子层数组。


层的几何转换(Transforming a Layer’s Geometry)


一旦你创建了一个layer,你就可以使用矩阵转换来转换layer的几何坐标。Transform数据结构定义了一个三维变换(是一个4行4列的CGFloat值),被用来对layer做旋转,放缩,偏移,歪斜和角度转换。

struct CATransform3D

{

CGFloat m11, m12, m13, m14;

CGFloat m21, m22, m23, m24;

CGFloat m31, m32, m33, m34;

CGFloat m41, m42, m43, m44;

};


有两个layer属性来指定变换矩阵:transform和sublayerTransform。通过transform属性指定的矩阵应用于layer以及与层的anchorPoint有关的该layer的子类。图3中展示用默认的anchorPoint(0.5,0.5)的时候,layer在旋转和放缩的时候对layer的影响。图4中显示当anchorPoint为(0.0,0.0)的时候对于相同的转换矩阵对一个layer的影响。sublayerTransform属性指定的矩阵仅仅应用与layer的sublayer,而不是应用与layer本身。


你可以通过下面方式中的一种创建和修改CATransform3D数据结构:

(1)用CATransform3D函数

(2)直接修改数据结构的成员

(3)用key-value编码和key path


CATransform3DIdentity常量的值是一个单位矩阵,这个矩阵不能进行放缩,旋转,歪斜和角度的使用。对layer使用这个单位矩阵,layer会显示默认几何结构。

转换函数(transform functions)

在core animation中transform函数可以对矩阵进行操作。你可以用这些函数(表1)去构建一个矩阵,在稍后一些通过分别修改transform和sunlayerTransform属性将其应用与layer或者layer的子类。transform函数既可以操作CATransform3D数据结构也可以返回一个数据结构。这可以使你构建简单或者复杂的可重用的转换。

表1 用于转换,旋转和放缩的 CATransform3D transform 函数

5A9DCDE9-4557-4AA8-88AB-914AAED0E9F6.png

旋转的角度是指的弧度而不是度。下面的函数允许你在弧度与度之间进行转换

CGFloat DegreesToRadians(CGFloat degrees) {return degrees * M_PI / 180;};


CGFloat RadiansToDegrees(CGFloat radians) {return radians * 180 / M_PI;};


core animation提供了一个反转函数用来反转一个矩阵,CATransform3DInvert,反转通常是使用在反转对象中提供的反转点。当你要恢复一个被矩阵转换过的值的时候转换是很有用的:反转矩阵和逆矩阵乘以这个值,得到的结果都是初始值。


函数也提供了允许你转换CATransform3D的矩阵到CGAffineTransform矩阵,如果CATransform3D矩阵可以这样表示。

表2  对于CGAffineTransform 转换的CATransform3D 转换函数

A2369DC8-DF98-41DC-B0D4-25C3F94FF931.png

修改转换数据结构(Modifying the Transform Data Structure)

列表1包含CATransform3D数据结构的定义,结构成员在其相应的矩阵位置。

列表1 CATransform3D结构


struct CATransform3D

{

  CGFloat m11, m12, m13, m14;

  CGFloat m21, m22, m23, m24;

  CGFloat m31, m32, m33, m34;

  CGFloat m41, m42, m43, m44;


}


typedef struct CATransform3D CATransform3D;


列表2中的例子说明了如何配置CATransform3D作为视角的转换。

列表2 直接修改CATransform3D数据结构

CATransform3D aTransform = CATransform3DIdentity;


// the value of zDistance affects the sharpness of the transform.


zDistance = 850;


aTransform.m34 = 1.0 / -zDistance;


使用key paths修改转换(Modifying a Transform Using Key Paths)

core animation对key-value coding协议进行了扩展,允许通过key paths获取和设置一个layer的CATransform3D矩阵的公值(common value)。表4描述了layer的transform 和sublayerTransform属性的key path是键值编码和观察兼容的。

表4 CATransform3D 关键路径

CDB78E33-FF00-4217-9E45-BFF46602A4D5.png

你不能用objective-c 2.0的属性来指定结构的key path。这样是不能有任何效果的:

myLayer.transform.rotation.x=0;


而是必须使用下面这样的setValue:forKeyPath: 或者 valueForKeyPath: 

[myLayer setValue:[NSNumber numberWithInt:0] forKeyPath:@"transform.rotation.x"];


Layer-Tree层次结构(Layer-Tree Hierarchy)

随着layer可以直接对自己提供的可视内容和动画进行负责管理,layer同时也扮演着包含其他layer的角色,从而创建了layer的层次结构。


什么是Layer-Tree层次结构?(What Is a Layer-Tree Hierarchy?)

layer-tree是core animation,它等同于cocoa视图层次结构。正如NSView或UIView的实例有superview和subviews一样,core animation layer也有superlayer和sublayers。layer-tree提供了与视图层次结构许多相同的好处:

(1)复杂的界面可以用简单的layer来实现,避免了单片和复杂的子类化。由于layer自身具有复杂的合成功能,使它可以很好的使用这种‘stacking(堆)’的类型。

(2)每个layer相对于它的superlayer的坐标系都有它自己的坐标系。当某个layer转换的时候的,这个layer的sublayer也会随之转换。

(3)layer-tree是动态的,它可以随着程序的运行重新装配,layer可以被创建,作为layer的第一个sublayer添加到layer上,在添加其他的,同时也可以从layer-tree中remove掉。


在视图中显示layer(Displaying Layers in Views)

core animation没有提供方法在一个窗口中来显示layer,他们必须由视图来托管。当对视图进行搭配的时候,视图必须提供底层的实践处理,而layer指示提供内容的显示。


在ios的视图系统中是直接在core animation layer的上面创建的。每个UIView的实例都会自动创建一个CALayer类的实例,同时将这个CALayer类的实例作为视图的layer属性的值。当然你也可以根据自己的需要来添加sublayer。

在mac os x中你必须配置一个NSView的的实例能够托管一个layer。为了显示layer tree的root layer,你需要设置视图的layer,同时设置视图去使用layer。

表1 在视图中插入layer

// theView is an existing view in a window


// theRootLayer is the root layer of a layer tree


[theView setLayer: theRootLayer];

[theView setWantsLayer:YES];


在层次结构中增加和移除layer( Adding and Removing Layers from a Hierarchy)

如果仅仅是简单的实例化一个layer不需要将其插入到layer-tree中,相反如果你是在layer-tree中增加,插入,替换,移除layer,你可以使用在Table 1中数列出的方法:

Table 1 layer-tree管理方法

F361152D-C353-497D-8CC7-9E1B69CB8371.png

你可以使用一个layer的数组来设置一个layer的sublayer,同时设置打算要设置superlayer的sublayer的属性。当用一个数组(数组中元素都是layer对象)来设置sublayer属性的时候,你必须确保数组中layer的superlayer设置为nil。

 


默认情况下,从一个可视的layer-tree中插入或者移除layers会触发一个动。当一个layer作为一个sublayer添加的时候,它的父layer的操作标识符kCAOnOrderIn会被触发并以动画的形式返回。当从layer的sublayers中移除一个layer的时候,它的父layer的操作标识符kCAOnOrderOut会被触发并以动画的形式返回。当在sublayer中替换一个layer的时候,它的父layer的操作标识符kCATransition会被触发并以动画的形式返回。当你在对layer-tree进行操作时候你可以禁止动画,或者使用任意的操作标识符来改变动画。


重新定位layer的位置和调整layer的尺寸(Repositioning and Resizing Layers)

layer被创建之后,你可以通过改变layer的几何属性(frame,bounds,position,anchorPoint,zPosition)来移动和调整它。

如果layer的needsDisplayOnBoundsChange属性设置为YES,那么当layer的bounds改变的时候,layer上的内容是要重新进行缓存处理的。默认情况下needsDisplayOnBoundsChange属性是NO。

默认情况下,设置frame,bounds,position,anchorPoint和zPosition属性会导致layer以这些新设置的值产生动画。


自动调整layer的大小 (Autoresizing layers)

CAlayer提供了一种自动的机制,就是当他们的superlayer在移动或者改变大小的时候,他们的sublayer也会自动的移动或者改变大小。在许多情况下,只需为layer配置自动调整的mask,就可以为应用程序提供适当的行为。layer的自动调整的mask是通过使用位运算or操作符或者使用layer的autoresizingMask属性的值来合并CAAutoresizingMask常量来指定的。

Table 2 Autoresizing mask values and descriptions

BE7BE17B-B764-40A2-B75D-B6C97D4A12F9.png

 例如,为了确保layer的superlayer在左下角,你可以使用kCALayerMaxXMargin | kCALayerMaxYMargin来设置。当沿着x轴方向不止一个方向是灵活的时候,会给每个方向都进行调整。

 

Figure 1 layer autoresizing mask constants

0CA051C0-03B8-4B4C-B63F-1DF35CC30711.png

  如果这些常量中有一个被省略,那么这个方向的布局就是不变的,当一个常量被包含的时候,那么layer的布局在这个方向上就是灵活的。


CAlayer的子类可以重写resizeSublayersWithOldSize: 和resizeWithOldSuperlayerSize: 方法从而来自定义layer的自动调整的大小的行为。layer的bounds属性改变的时候,layer的resizeSublayersWithOldSize:方法都会被自动调用,同时也会给每个sublayer发送一个resizeWithOldSuperlayerSize:消息。每个layer都会用以前的bounds的值与现在的bounds的值进行比较,从而来根据它的autoresize mask来调整它的位置和大小。



裁剪子层(Clipping Sublayers)

当cocoa view的subviwes处于父view的bounds的外面时,这些subviews会被父view所裁剪。layer移除了这个限制,允许sublayer显示他们的全部整体,不用管他们相对于父layer的位置。


layer的masksToBounds属性决定了sublayer是否被父layer所裁剪,masksToBounds的默认值是NO,防止sublayer被父layer裁剪。

Figure  2  masksToBounds属性值举例

CDE99249-7EE4-456A-80A8-4E550871B458.png

提供layer中的显示内容(Providing Layer Content)

当使用cocoa视图的时候你必须对NSView或者UIView进行子类化,同时为了显示内容还必须完成drawRect:方法。但是CALayer实例都是直接使用的,并不需要子类化。因为CALAyer是一个键值编码兼容的容器类,你可以在任何实例中存储任意的值,避免了直接子类化。


提供layer中的内容(Providing Layer Content)

你可以使用下面方法中的一种来指定CALayer的内容:

(1)用包含Image的CGImageRef来设置layer的content属性

(2)使用代理来绘制cotents

(3)对CAlayer进行子类化并重写显示的方法。


设置contents属性(Setting the Contents Property)

通过contents属性设置到CGImageRef来指定layer所包含的图像

Listing 1 设置layer的contents属性

CALayer *theLayer;
// create the layer and set the bounds and position
theLayer=[CALayer layer];
theLayer.position=CGPointMake(50.0f,50.0f);
theLayer.bounds=CGRectMake(0.0f,0.0f,100.0f,100.0f);
// set the contents property to a CGImageRef
// specified by theImage (loaded elsewhere)
theLayer.contents=theImage;


使用代理提供content(Using a Delegate to Provide Content)

你可以为你的layer绘制content,或者创建一个代理类来完成下面的方法中displayLayer:或drawLayer:inContext:中的一个来设置layer的content图像。

layer的绘制的时候并不会自动就调用已经完成了绘制content的代理方法,而是,你必须通过发送setNeedsDisplay 或 setNeedsDisplayInRect:消息,或者设置needsDisplayOnBoundsChange属性值为YES,来通知layer的实例去重新缓存它的content。

完成的displayLayer:代理方法能够决定哪个image应该显示在哪个指定的layer上,同时相应的设置layer的contents属性。在“layer Coordinate system”中实施displayLayer:这个例子是通过state key来设置thelayer的contents属性值的。子类化的时候不需要存储一个state值,因为CALayer实例是一个键值编码容器类。

Listing 2 完成代理方法displayLayer:的例子

- (void)displayLayer:(CALayer *)theLayer
{
    // check the value of the layer's state key
    if ([[theLayer valueForKey:@"state"] boolValue])
    {
        // display the yes image
        theLayer.contents=[someHelperObject loadStateYesImage];
    }
    else {
        // display the no image
        theLayer.contents=[someHelperObject loadStateNoImage];
    }
}


如果你是自己绘制layer的content而不是从一个image加载的话,你就要完成drawLayer:inContext: 代理方法。这个代理参数是被绘制的layer和CGContextRef

下面这个例子用drawLayer:inContext: 来完成的。

Listing 3 完成代理方法drawLayer:inContext: 的例子

- (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,
                          [[theLayer valueForKey:@"lineWidth"] floatValue]);
    CGContextStrokePath(theContext);
  // release the path
    CFRelease(thePath);
}

关注下方微信公众号“Java精选”(w_z90110),回复关键字领取资料:如HadoopDubboCAS源码等等,免费领取资料视频和项目。 

涵盖:程序人生、搞笑视频、算法与数据结构、黑客技术与网络安全、前端开发、Java、Python、Redis缓存、Spring源码、各大主流框架、Web开发、大数据技术、Storm、Hadoop、MapReduce、Spark、elasticsearch、单点登录统一认证、分布式框架、集群、安卓开发、iOS开发、C/C++、.NET、Linux、Mysql、Oracle、NoSQL非关系型数据库、运维等。

评论

分享:

支付宝

微信