ios 核心动画

来源:互联网 发布:知春大厦兼职是真的吗 编辑:程序博客网 时间:2024/03/29 19:32
引用地址:http://blog.csdn.net/zkdemon/article/details/7446319
核心动画,开发人员可以为他们的应用创建动态用户界面,而无需使用低级别的图形 API,如 OpenGL 来获取高效的动画性能。 

前言,核心动画的好处

1.简单易用的高性能混合编程模型。

2.类似视图一样,你可以通过使用图层来创建复杂的接口。   通过是CALayer来使用更复杂的一些动画。   

3.轻量级的数据结构,它可以同时显示并让上百个图层产生动画效果。     控制多个CALayer来显示动画效果       

4.一套简单的动画接口,可以让你的动画运行在独立的线程里面,并可以独立于主线程之外。

5.一旦动画配置完成并启动,核心动画完全控制并独立完成相应的动画帧。

6.提高应用性能。应用程序只当发生改变的时候才重绘内容。再小的应用程序也需要改变和提供布局服务层。核心动画还消除了在动画的帧速率上运行的应用程序代码。

7.灵活的布局管理模型。包括允许图层相对同级图层的关系来设置相应属性的位置和大小。   可以使用CALayer来更灵活的进行布局。  

1.1 Core animation类

1.提供显示内容的图层类。CALayer

2.动画和计时类。Animation and Timing Classes

3.布局和约束类。

4.事务类,在原子更新的时候组合图层类。核心动画的基础类包含在 Quartz 核心框架(Quartz Core framework)里面,虽然它的其他图层类在其他框架里面定义。下图显示了核心动画的类层次结构。 

 1.1.1 图层类   

层类(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(层的投影)。   

图层的内容提供

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

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

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

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

 

  三个重要的子类

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

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

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

 

   Mac OS X 额外的类CAOpenGLLayer 提供了一个OpenGL渲染环境。你必须继承这个类来使用 OpenGL 提供的内容。内容可以是静态的,或可随着时间的推移更新。QCCompositionLayer(由Quartz框架提供)可以把Quartz合成的内容动画 显示。QTMovieLayer and QTCaptureLayer (QTKit 框架提供)提供播放 QuickTime 影片和视频直播。

 

   iOS 独特的CALayerCAEAGLLayer 提供了一个OpenGLES渲染环境。CALayer 的类引入键-值编码兼容的容器类概念,也就是说一个类可以使用键 - 值编码的方法存储任意值,而无需创建一个子类。CALayer 的还扩展了 NSKeyValueCoding 的非正式协议,加入默认键值和额外的结构类型的自动对象包装 (CGPoint,CGSize,CGRect,CGAffineTransform 和 CATransform3D)的支持,并 提供许多这些结构的关键路径领域的访问。CALayer也管理动画和与其相关的layer的actions。layers接收一些从layer tree中触发的insert和remove消息,修改被创建的layer的属性,或者指明开发者的需求。这些actions通常都会导致动画的产生。

 

1.1.2 动画和计时类(Animation and Timing Classes)     图层的很多可视化属性是可以隐式动画的。通过简单的改变图层的可动画显示的属性,可以让图层现有属性从当前值动画渐变到新的属性值。例如设置图层的 hidden 属性为 YES 将会触发动画使层逐渐淡出。

    默认动画 

   大多数动画属性拥有自己关联的默认动画, 你可以轻松地定制和替换。我们将会在后面“动画属性”部分列出一个完整的动画属性列表和它们相应的默认动画。

   关于哪些属性执行的是什么默认动画效果请参考apple官方文档 

   显式动画

   动画的属性也可以显式动画。要显式动画的属性,你需要创建核心动画动画类的一个实例,并指定所需的视觉效果。显式动画不会改变该 、属性的值,它只是用于动画显示。 

 

    核心动画的动画类使用基本的动画和关键帧动画把图层的内容和选取的属性动画的显示出来。所有核心动画的动画类都是从 CAAnimation 类继承而来。

CAAnimation 实现了 CAMediaTiming 协议,提供了动画的持续时间,速度,和重复计数。 CAAnimation 也实现了 CAAction 协议。该协议为图层触发一个动画动作提供了提供标准化响应。动画类同时定义了一个使用贝塞尔曲线来描述动画改变的时间函数。例如,一个 匀速时间函数(linear timing function)在动画的整个生命周期里面一直保持速度不变, 而渐缓时间函数(ease-out timing function)则在动画接近其生命周期的时候减慢速度。核心动画额外提供了一系列抽象的和细化的动画类,比如:CATransition 提供了一个图层变化的过渡效果,它能影响图层的整个内容。 动画进行的时候淡入淡出(fade)、推(push)、显露(reveal)图层的内容。这些过渡效 果可以扩展到你自己定制的 Core Image 滤镜。CAAnimationGroup 允许一系列动画效果组合在一起,并行显示动画。

 

多个动画效果叠加,比如在执行动画的过程中需要同时修改position,alpha, frame等属性,可以将三个动画合成一起执行。CAAnimationGroup *animGroup [CAAnimationGroup animation]; 

animGroup.animations [NSArray arrayWithObjects:moveAnim,scaleAnim,opacityAnim, nil];
animGroup.duration 1;
[view.layer addAnimation:animGroup forKey:nil];

     CAPropertyAnimation 是一个抽象的子类,它支持动画的显示图层的关键路 径中指定的属性一般不直接使用,而是使用它的子类,CABasicAnimation,CAKeyframeAnimation. 在它的子类里修改属性来运行动画。

 

CABasicAnimation 简单的为图层的属性提供修改。 很多图层的属性修改默认会执行这个动画类。比如大小,透明度,颜色等属性

 CAKeyframeAnimation 支持关键帧动画,你可以指定的图层属性的关键路径动画,包括动画的每个阶段的价值,以及关键帧时间和计时功能的一系列值。在 动画运行是,每个值被特定的插入值替代。核心动画 和 Cocoa Animation 同时使用这些动画类。使用动画描述,是因为这些类涉及到核心动画,这些将会在Animation Types and Timing Programming Guide 有较深入的讨论。

 

1.1.3 布局管理器类Application Kit 的视图类相对于 superlayer 提供了经典的“struts and springs”定位 模型。图层类兼容这个模型,同时 Mac OS X 上面的核心动画提供了一套更加灵活 的布局管理机制,它允许开发者自己修改布局管理器。核心动画的 CAConstraint 类 是一个布局管理器,它可以指定子图层类限制于你指定的约束集合。每个约束 (CAConstraint 类的实例封装)描述层的几何属性(左,右,顶部或底部的边缘或水 平或垂直中心)的关系,关系到其同级之一的几何属性层或 superlayer。通用的布局管理器和约束性布局管理器将会在“布局核心动画的图层”部分讨论。

 1.1.4 事务管理类     图层的动画属性的每一个修改必然是事务的一个部分。CATransaction 是核心动画里面负责协调多个动画原子更新显示操作。事务支持嵌套使用。     



第二章 核心动画渲染框架 可能有人会很好奇CoreAnimation是如何渲染动画,动画是如何生成的。 在core aniamtion和cocoa view之间有很大的相似之处,他们之间最大的概念上的分歧就是layer不直接渲染到屏幕上。在MVC的设计模式下,NSView和UIView是视图对象,core animation层实际上是模型对象。他们封装了几何图形,时间和可视属性,同时提供显示的内容,但是实际上显示并不是layer的责任。每一个可视的layer tree后面都有两个相应的tree:presentation tree和render tree。如下图所示:

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的实例。如果你打算改变当前的动画并且从当前显示状态开始一个新的动画,这是非常有用的。   


第三章  图层的几何和变化

 

 图层的几何变化涉及到修改图层的几何属性,比如大小,锚点,圆角等属性等等,这个部分详细的可以查看Quartz 2D 的文档,这个文档讲的比较详细。 

 3.1图层的坐标系

图层的坐标系在不同平台上面具有差异性。在 iOS 系统中,默认的坐标系统原点 在图层的中心左上角地方,原点向右和向下为正值。在 Mac OS X 系统中,默认的坐 标系原点在图层的中心左下角地方,原点向右和向上为正值。坐标系的所有值都是浮 点类型。你在任何平台上面创建的图层都采用该平台默认的坐标系。

每个图层定义并维护自己的坐标系,它里面的全部内容都由此相关的坐标系指定 位置。该准则同时适应于图层自己的内容和它的任何子图层。因为任何图层定义了它 自己的坐标系,CALayer 类提供相应的方法用于从一个图层坐标系的点、矩形、大小 值转化为另一个图层坐标系相应的值。一些基于图层的属性使用单元坐标空间测量它们的值。单元坐标空间指定图层边 界的相对值,而不是绝对值。单元坐标空间给定的 x 和 y 的值总是在 0.0 到 1.0 之间。 指定一个沿 X 轴的值为 0.0 的点,得到的是图层左边缘的一个点,而指定一个 1.0 的点,则是图层右边缘的一个点。(对 Y 轴而言,如果是在 iOS 系统,则 0.0 对应于 顶部的点,而 1.0 则是底部的点,而在 Mac OS X 系统,得到的刚好相反,就如之前 提到的坐标系不同一样)。而点(0.5,0.5)则刚好是图层的中心点。

 

 3.2指定图层的几何

虽然图层和图层树与视图和视图的结构在很多方面具有相似性,但是图层的几何 却不同,它更加简单通俗。图层的所有几何属性,包括图层的矩阵变换,都可以隐式 和显式动画。

  下图显示可以在上下文中指定图层几何的属性: 

图层的 position 属性是一个 CGPoint 的值,它指定图层相当于它父图层的位置, 该值基于父图层的坐标系。图层的 bounds 属性是一个 CGRect 的值,指定图层的大小(bounds.size)和图层的 原点(bounds.origin)。当你重写图层的重画方法的时候,bounds 的原点可以作为图形 上下文的原点。图层拥有一个隐式的 frame,它是 position,bounds,anchorPoint 和 transform 属性 的一部分。设置新的 frame 将会相应的改变图层的 position 和 bounds 属性,但是 frame 本身并没有被保存。但是设置新的 frame 时候,bounds 的原点不受干扰,bounds 的大 小变为 frame 的大小,即 bounds.size=frame.size。图层的位置被设置为相对于锚点 (anchor point)的适合位置。当你设置 frame 的值的时候,它的计算方式和 position、 bounds、和 anchorPoint 的属性相关。图层的 anchorPoint 属性是一个 CGPoint 值,它指定了一个基于图层 bounds 的符 合位置坐标系的位置。锚点(anchor point)指定了 bounds 相对于 position 的值,同 时也作为变换时候的支点。锚点使用单元空间坐标系表示,(0.0,0.0)点接近图层 的原点,而(1.0,1.0)是原点的对角点。改变图层的父图层的变换属性(如果存在 的话)将会影响到 anchorPoint 的方向,具体变化取决于父图层坐标系的 Y 轴。 当你设置图层的 frame 属性的时候,position 会根据锚点(anchorPoint)相应的改 变,而当你设置图层的 position 属性的时候,bounds 会根据锚点(anchorPoint)做相应的改变。 

 

下图描述了基于锚点的三个示例值:

anchorPoint 默认值是(0.5,0.5),位于图层边界的中心点(如上图显示),B 点 把 anchorPoint 设置为(0.0,0.5)。最后 C 点(1.0,0.0)把图层的 position 设置为 图层 frame 的右下角。该图适用于 Mac OS X 的图层。在 iOS 系统里面,图层使用不 同的坐标系,相应的(0.0,0.0)位于左上角,而(1.0,1.0)位于右下角。

图层的 frame、bounds、position 和 anchorPoint 关系如下图所示: 

在该示例中,anchorPoint 默认值为(0.5,0.5),位于图层的中心点。图层的 position 值为(100.0,100.0),bounds 为(0.0,0.0,120,80.0)。通过计算得到图层的 frame为(40.0,60.0,120.0,80.0)。

如果你新创建一个图层,则只有设置图层的 frame 为(40.0,60.0,120.0,80.0),相应的 position 属性值将会自动设置为(100.0,100.0),而 bounds 会自动设置为 (0.0,0.0,120.0,80.0)。下图显示一个图层具有相同的 frame(如上图),但是在该图中它的 anchorPoint 属性值被设置为(0.0,0.0),位于图层的左下角位置。

 

图层的 frame 值同样为(40.0,60.0,120.0,80.0),bounds 的值不变,但是图层的 position 值已经改变为(40.0,60.0)。

图层的几何外形和 Cocoa 视图另外一个不同地方是,你可以设置图层的一个边角 的半径来把图层显示为圆角。图层的 cornerRadius 属性指定了重绘图层内容,剪切子 图层,绘制图层的边界和阴影的时候时候圆角的半径。

图层的 zPosition 属性值指定了该图层位于 Z 轴上面位置,zPosition 用于设置图层 相对于图层的同级图层的可视位置

3.3 图层的几何变换

图层一旦创建,你就可以通过矩阵变换来改变一个图层的几何形状。 CATransform3D 的数据结构定义一个同质的三维变换(4x4 CGFloat 值的矩阵),用于 图层的旋转,缩放,偏移,歪斜和应用的透视。图层的两个属性指定了变换矩阵:transform 和 sublayerTransform 属性。图层的 transform 属性指定的矩阵结合图层的 anchorPoint 属性作用于图层和图层的子图层上 面。图 3 显示在使用 anchorPoint 默认值(0.5,0.5)的时候旋转和缩放变换如何影响一个图层。而图 4 显示了同样的矩阵变换在 anchorPoint 为(0.0,0.0)的时候如何改变一 个图层。图层的 sublayerTransform 属性指定的矩阵只会影响图层的子图层,而不会对 图层本身产生影响。你可以通过以下的任何一个方法改变 CATransform3D 的数据结构:(1) 使用CATransform3D函数(2) 直接修改数据结构的成员(3) 使用键-值编码改变键路径

CATransform3DIdentity 是单位矩阵,该矩阵没有缩放、旋转、歪斜、透视。把该 矩阵应用到图层上面,会把图层几何属性修改为默认值。 

 3.3.1 变换函数

使用变换函数可以在核心动画里面在操作矩阵。你可以使用这些函数(如下表)去 创建一个矩阵一般后面用于改变图层或者它的子图层的 transform 和 sublayerTransform属性。变换函数或者直接操或者返回一个CATransform3D的数据结 构。这可以让你能够构建简单或复杂的转换,以便重复使用。

核心动画 提供了用于转换矩阵的变换函数 CATransform3DInvert。一般是用反转 点内转化对象提供反向转换。当你需要恢复一个已经被变换了的矩阵的时候,反转将 会非常有帮助。反转矩阵乘以逆矩阵值,结果是原始值。变换函数同时允许你把 CATransform3D 矩阵转化为 CGAffineTransform(仿射) 矩阵,前提是 CATransform3D 矩阵采用如下表示方法。

变换函数同时提供了可以比较一个变换矩阵是否是单位矩阵,或者两个矩阵是否 相等。

3.3.2 修改变换的数据结构你可以修改 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 一个角度变换。             

3.3.3 通过键值路径修改变换   核心动画扩展了键-值编码协议,允许通过关键路径获取和设置一个图层的 CATransform3D 矩阵的值。表 4 描述了图层的 transform 和 sublayerTransform 属性的相应关键路径。  

 你不可以通过 Objective-C 2.0 的属性来设置结构域的值,比如下面的代码将会无法正常运行:      myLayer.transform.rotation.x=0;        替换的办法是,你必须通过 setValue:forKeyPath:或者 valueForKeyPath:方法, 具体如下:         [myLayer setValue:[NSNumber numberWithInt:0] forKeyPath:@"transform.rotation.x"];

 

 3.4 图层的操作

图层有一个图层树,既然是树,就允许添加,插入,删除,替换相应的图层,

 这些操作都可以通过已有的api实现,api如下


3.5 图层的内容 图层的内容提供,是指通过一种方法来制定CALayer 实例的内容: 其中有一下三种方式来提供CAlayer的内容  (1)使用包含图片内容的 CGImageRef 来显式的设置图层的 contents 的属性。   (2)指定一个委托,它提供或者重绘内容。  (3)继承 CALayer 类重载显示的函数。

 

第四章 动画动画是当今用户界面的关键因素。当使用核心动画的时候,动画是自动完成的。 没有动画的循环和计数器。你的应用程序不负负责重绘,也不负责跟踪动画的当前状 态。动画在独立线程里面自动执行,没有和你的应用程序交互。核心动画提供了一套你可以在你应用程序里面使用的动画类的表现:  (1)CABasicAnimation提供了在图层的属性值间简单的插入。  (2)CAKeyframeAnimation 提供支持关键帧动画。你指定动画的一个图层属性的关键路径,一个表示在动画的每个阶段的价值的数组,还有一个关键帧时间的数组和时间函数。  (3)CATransition提供了一个影响整个图层的内容过渡效果。在动画显示过程中采用淡出(fade)、推出(push)、显露(reveal)图层的内容。 常用的过渡效果可以通过提供你自己定制的核心图像滤镜来扩展。除了要指定显示的动画类型,你还必须指定动画的间隔、它的速度(它的插值如何分布在整个动画过程)、动画循环时候的循环次数、动画周期完成的时候是否自动 的反转、还有动画结束的时候它的可视化状态。动画类和 CAMediaTiming 协议提供 所有这些功能甚至更多的功能。CAAnimation、它的子类、时序协议被核心动画和Cocoa Animation Proxy功能共享。这些类将会在“动画类型和时序编程指南(Animation Types and Timing Programming Guide)”里面详细介绍。

             Core Animation 类的继承关系图 

 

下面将针对上面的知识做一个图片动画的demo

功能1 :移动图片到右下角

//向右下角缩小移动 - (IBAction)buttonClick:(id)sender{        CGPoint fromPoint = imageView.center;        //路径曲线     UIBezierPath *movePath = [UIBezierPath bezierPath];    [movePath moveToPoint:fromPoint];     CGPoint toPoint = CGPointMake(300, 460);    [movePath addQuadCurveToPoint:toPoint                     controlPoint:CGPointMake(toPoint.x,fromPoint.y)];       //关键帧     CAKeyframeAnimation *moveAnim = [CAKeyframeAnimation animationWithKeyPath:@"position"];    moveAnim.path = movePath.CGPath;    moveAnim.removedOnCompletion = YES;        //旋转变化     CABasicAnimation *scaleAnim = [CABasicAnimation animationWithKeyPath:@"transform"];    scaleAnim.fromValue = [NSValue valueWithCATransform3D:CATransform3DIdentity];    //x,y轴缩小到0.1,Z 轴不变     scaleAnim.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(0.1, 0.1, 1.0)];    scaleAnim.removedOnCompletion = YES;        //透明度变化     CABasicAnimation *opacityAnim = [CABasicAnimation animationWithKeyPath:@"alpha"];    opacityAnim.fromValue = [NSNumber numberWithFloat:1.0];    opacityAnim.toValue = [NSNumber numberWithFloat:0.1];    opacityAnim.removedOnCompletion = YES;        //关键帧,旋转,透明度组合起来执行     CAAnimationGroup *animGroup = [CAAnimationGroup animation];    animGroup.animations = [NSArray arrayWithObjects:moveAnim, scaleAnim,opacityAnim, nil];    animGroup.duration = 1;    [imageView.layer addAnimation:animGroup forKey:nil];} 

 功能2 :移动图片到右下角

 //向右边旋转 - (IBAction)rightRotateBtnClick:(id)sender{    CGPoint fromPoint = imageView.center;    UIBezierPath *movePath = [UIBezierPath bezierPath];    [movePath moveToPoint:fromPoint];    CGPoint toPoint = CGPointMake(fromPoint.x +100 , fromPoint.y) ;        [movePath addLineToPoint:toPoint];        CAKeyframeAnimation *moveAnim = [CAKeyframeAnimation animationWithKeyPath:@"position"];    moveAnim.path = movePath.CGPath;    CABasicAnimation *scaleAnim = [CABasicAnimation animationWithKeyPath:@"transform"];    scaleAnim.fromValue = [NSValue valueWithCATransform3D:CATransform3DIdentity];        //沿Z轴旋转     scaleAnim.toValue = [NSValue valueWithCATransform3D: CATransform3DMakeRotation(M_PI,0,0,1)];        //沿Y轴旋转    // scaleAnim.toValue = [NSValue valueWithCATransform3D: CATransform3DMakeRotation(M_PI,0,1.0,0)];         //沿X轴旋转   // scaleAnim.toValue = [NSValue valueWithCATransform3D: CATransform3DMakeRotation(M_PI,1.0,0,0)];     scaleAnim.cumulative = YES;    scaleAnim.duration =1;  //旋转2遍,360度         scaleAnim.repeatCount =2;    imageView.center = toPoint;    scaleAnim.removedOnCompletion = YES;    CAAnimationGroup *animGroup = [CAAnimationGroup animation];    animGroup.animations = [NSArray arrayWithObjects:moveAnim, scaleAnim, nil];    animGroup.duration = 2;        [imageView.layer addAnimation:animGroup forKey:nil];}

功能3 图片旋转360度

//图片旋转360度 - (IBAction)rota360BtnClick:(id)sender{    CABasicAnimation *animation = [ CABasicAnimation                                   animationWithKeyPath: @"transform" ];    animation.fromValue = [NSValue valueWithCATransform3D:CATransform3DIdentity];        //围绕Z轴旋转,垂直与屏幕     animation.toValue = [ NSValue valueWithCATransform3D: CATransform3DMakeRotation(M_PI, 0, 0, 1.0) ];    animation.duration = 1;    //旋转效果累计,先转180度,接着再旋转180度,从而实现360旋转    animation.cumulative = YES;   animation.repeatCount = 2;        //在图片边缘添加一个像素的透明区域,去图片锯齿     CGRect imageRrect = CGRectMake(0, 0,imageView.frame.size.width, imageView.frame.size.height);    UIGraphicsBeginImageContext(imageRrect.size);     [imageView.image drawInRect:CGRectMake(1,1,imageView.frame.size.width-2,imageView.frame.size.height-2)];    imageView.image = UIGraphicsGetImageFromCurrentImageContext();    UIGraphicsEndImageContext();       [imageView.layer addAnimation:animation forKey:nil];}
原创粉丝点击