• Swift语言iOS开发:CALayer十则示例


    如你所知,我们在iOS应用中看到的都是视图(view),包括按钮视图、表视图、滑动条视图,还有可以容纳其他视图的父视图等。

    AD:【活动】Web和APP兼容性实战 Win10训练营免费报名

    如你所知,我们在iOS应用中看到的都是视图(view),包括按钮视图、表视图、滑动条视图,还有可以容纳其他视图的父视图等。

    但你或许不知道在iOS中支撑起每个视图的是一个叫做"图层(layer)"的类,确切地说是CALayer。

    本文中您会了解CALayer及其工作原理,还有应用CALayer打造酷炫效果的十则示例,比如绘制矢量图形、渐变色,甚至是粒子系统。

    本文要求读者熟悉iOS应用开发和Swift语言的基础知识,包括利用Storyboard构建用户界面。

    注:如果您尚未掌握这些基础,不必担心,我们有不少相关教程,例如使用Swift语言编写iOS应用和iOS学徒。

    准备开始

    要理解图层是什么,最简便的方式就是"实地考察"。我们这就创建一个简单的项目,从头开始玩转图层。

    准备好写代码了吗?好!启动Xcode,然后:

    1.选择FileNewProject菜单项。

    2.在对话框中选择iOSApplicationSingle View Application。

    3.点击Next,Product Name填写CALayerPlayground,然后输入你自己的Organization Name和Identifier。

    4.Language选Swift,Devices选Universal。

    5.取消选择Core Data,点击Next。

    6.把项目保存到合适的位置(个人习惯把项目放在用户目录下建立的Source文件夹),点击Create。

    好,文件准备就绪,接下来就是创建视图了:

    7.在项目导航栏(Project navigator)中选择Main.storyboard。

    8.选择ViewAssistant EditorShow Assistant Editor菜单项,如果没有显示对象库(Object Library),请选择ViewUtilitiesShow Object Library。

    9.然后选择EditorCanvasShow Bounds Rectangles,这样在向场景添加视图时就可以看到轮廓了。

    10.把一个视图(View)从对象库拖入视图控制器场景,保持选中状态,在尺寸检查器(ViewUtilitiesShow Size Inspector)中将x和y设为150,Width和Height设为300。

    11.视图保持选中,点击自动布局工具栏(Storyboard右下角)的Align按钮,选中Horizontal Center in Container和Vertical Center in Container,数值均为0,然后点击Add 2 Constraints。

    12.点击Pin按钮,选中Width和Height,数值均设为300,点击Add 2 Constraints。

    最后按住control从刚刚创建的视图拖到ViewController.swift文件中viewDidLoad()方法的上方,在弹框中将outlet命名为viewForLayer,如图:

    点击Connect创建outlet。

    将ViewController.swift中的代码改写为:

    1. import UIKit 
    2.  
    3. class ViewController: UIViewController { 
    4.  
    5. @IBOutlet weak var viewForLayer: UIView! 
    6.  
    7. var l: CALayer { 
    8. return viewForLayer.layer 
    9.  
    10. override func viewDidLoad() { 
    11. super.viewDidLoad() 
    12. setUpLayer() 
    13.  
    14. func setUpLayer() { 
    15. l.backgroundColor = UIColor.blueColor().CGColor 
    16. l.borderWidth = 100.0 
    17. l.borderColor = UIColor.redColor().CGColor 
    18. l.shadowOpacity = 0.7 
    19. l.shadowRadius = 10.0 
    20.  

    之前提到iOS中的每个视图都拥有一个关联的图层,你可以通过yourView.layer访问图层。这段代码首先创建了一个叫"l"(小写L)的计算属性,方便访问viewForLayer的图层,可让你少写一些代码。

    这段代码还调用了setUpLayer方法设置图层属性:阴影,蓝色背景,红色粗边框。你马上就可以了解这些东西,不过现在还是先构建App,在iOS模拟器中运行(我选了iPhone 6),看看自定义的图层如何。

    几行代码,效果还不错吧?还是那句话,每个视图都由图层支撑,所以你也可以对App中的任何视图做出类似修改。我们继续深入。

    CALayer基本属性

    CALayer有几个属性可以用来自定外观,想想刚才做的:

    把图层背景色从默认的无色改为蓝色

    通过把边框宽度从默认的0改为100来添加边框

    把边框颜色从默认的黑色改为红色

    最后把阴影透明度从0(全透明)改为0.7,产生阴影效果,此外还把阴影半径从默认的3改为10。

    以上只是CALayer中可以设置的部分属性。我们再试两个,在setUpLayer()中追加以下代码:

    1. l.contents = UIImage(named: "star")?.CGImage 
    2. l.contentsGravity = kCAGravityCenter 

    CALayer的contents属性可以把图层的内容设为图片,这里我们要设置一张"星星"的图片,为此你需要把图片添加到项目中,请下载图片并添加到项目中。

    构建,运行,欣赏一下效果:

    注意星星居中,这是因为contentsGravity属性被设为kCAGravityCenter,如你所想,重心也可以设为上、右上、右、右下、下、左下、左、左上。

    更改图层外观

    仅供娱乐,我们来添加几个手势识别器来控制图层外观。在Xcode中,向viewForLayer对象上拖一个轻触手势识别器(tap gesture recognizer),见下图:

    注:如果你对手势识别器比较陌生,请参阅Using UIGestureRecognizer with Swift。

    以此类推,再添加一个捏合手势识别器(pinch gesture recognizer)。

    然后按住control依次将两个手势识别器从Storyboard场景停靠栏拖入ViewController.swift,放在setUpLayer()和类自身的闭合花括号之间。

    在弹框中修改连接为Action,命名轻触识别操作为tapGestureRecognized,捏合识别操作为pinchGestureRecognized,例如:

    如下改写tapGestureRecognized(_:):
     

    1. @IBAction func tapGestureRecognized(sender: UITapGestureRecognizer) { 
    2. l.shadowOpacity = l.shadowOpacity == 0.7 ? 0.0 : 0.7 

    当令视图识别出轻触手势时,代码告知viewForLayer图层在0.7和0之间切换阴影透明度。

    你说视图?嗯,没错,重写CALayer的hitTest(_:)也可以实现相同效果,本文后面也会看到这个方法,不过我们这里用的方法也有道理:图层本身并不能响应手势识别,只能响应点击测试,所以我们在视图上设置了轻触手势识别器。

    然后如下修改pinchGestureRecognized(_:):

    1. @IBAction func pinchGestureRecognized(sender: UIPinchGestureRecognizer) { 
    2. let offset: CGFloat = sender.scale < 1 ? 5.0 : -5.0 
    3. let oldFrame = l.frame 
    4. let oldOrigin = oldFrame.origin 
    5. let newOrigin = CGPoint(x: oldOrigin.x + offset, y: oldOrigin.y + offset) 
    6. let newSize = CGSize( oldFrame.width + (offset * -2.0), height: oldFrame.height + (offset * -2.0)) 
    7. let newFrame = CGRect(origin: newOrigin, size: newSize) 
    8. if newFrame.width >= 100.0 && newFrame.width <= 300.0 { 
    9. l.borderWidth -= offset 
    10. l.cornerRadius += (offset / 2.0) 
    11. l.frame = newFrame 

    此处基于用户的捏合手势创建正负偏移值,借此调整图层框架大小、边缘宽度和边角半径。

    图层的边角半径默认值为0,意即标准的90度直角。增大半径会产生圆角,如果想将图层变成圆形,可以设边角半径为宽度的一半。

    注意:调整边角半径并不会裁剪图层内容(星星图片),除非图层的masksToBounds属性被设为true。

    构建运行,尝试在视图中使用轻触和捏合手势:

    嘿,再好好装扮一下都能当头像用了! :]

    CALayer体验

    CALayer中的属性和方法琳琅满目,此外还有几个包含特有属性和方法的子类。

    要遍历如此酷炫的API,Raywenderlich.com导游先生最好不过了。

    接下来,你需要以下材料:

    Layer Player App

    Layer Player 源代码

    该App包含十种不同的CALayer示例,本文后面会依次介绍,十分方便。先来吊吊大家的胃口:

    下面在讲解每个示例的同时,我建议在CALayer演示应用中亲自动手试验,还可以读读代码。不用写,只要深呼吸,轻松阅读就可以了。 :]

    我相信这些酷炫的示例会启发您利用不同的CALayer为自己的App锦上添花,希望大家喜欢!

    示例 #1:CALayer

    前面我们看过使用CALayer的示例,也就是设置各种属性。

    关于CALayer还有几点没提:

    图层可以包含子图层。就像视图可以包含子视图,图层也可以有子图层,稍加利用就能打造漂亮的效果!

    图层属性自带动画效果。修改图层属性时,存在默认的动画效果,你也可以自定义动画行为。

    图层是轻量概念。相对视图而言,图层更加轻量,因此图层可以帮助提升性能。

    图层有大量实用属性。前面你已经看过几条了,我们继续探索!

    刚刚说CALayer图层有很多属性,我们来看一批实用属性:有些属性你可能第一次见,但真的很方便!

    1. // 1 
    2. let layer = CALayer() 
    3. layer.frame = someView.bounds 
    4.  
    5. // 2 
    6. layer.contents = UIImage(named: "star")?.CGImage 
    7. layer.contentsGravity = kCAGravityCenter 
    8.  
    9. // 3 
    10. layer.magnificationFilter = kCAFilterLinear 
    11. layer.geometryFlipped = false 
    12.  
    13. // 4 
    14. layer.backgroundColor = UIColor(red: 11/255.0, green: 86/255.0, blue: 14/255.0, alpha: 1.0).CGColor 
    15. layer.opacity = 1.0 
    16. layer.hidden = false 
    17. layer.masksToBounds = false 
    18.  
    19. // 5 
    20. layer.cornerRadius = 100.0 
    21. layer.borderWidth = 12.0 
    22. layer.borderColor = UIColor.whiteColor().CGColor 
    23.  
    24. // 6 
    25. layer.shadowOpacity = 0.75 
    26. layer.shadowOffset = CGSize( 0, height: 3) 
    27. layer.shadowRadius = 3.0 
    28. someView.layer.addSublayer(layer) 

    在以上代码中:

    创建一个CALayer实例,并把框架设为someView边框。

    将图层内容设为一张图片,并使其在图层内居中,注意赋值的类型是底层的Quartz图像数据(CGImage)。

    使用过滤器,过滤器在图像利用contentsGravity放大时发挥作用,可用于改变大小(缩放、比例缩放、填充比例缩放)和位置(中心、上、 右上、右等等)。以上属性的改变没有动画效果,另外如果geometryFlipped未设为true,几何位置和阴影会上下颠倒。继续:

    把背景色设为Ray最爱的深绿色。:] 然后让图层透明、可见。同时令图层不要遮罩内容,意思是如果图层尺寸小于内容(星星图片),图像不会被裁减。

    图层边角半径设为图层宽度的一半,使边缘变为圆形,注意图层颜色赋值类型为Quartz颜色引用(CGColor)。

    创建阴影,设shouldRasterize为true(后文还会提到),然后将图层加入视图结构树。

    结果如下:

    CALayer还有两个附加属性有助于改善性能:shouldRasterize和drawsAsynchronously。

    shouldRasterize默认为false,设为true可以改善性能,因为图层内容只需要一次渲染。相对画面中移动但自身外观不变的对象效果拔群。

    drawsAsynchronously默认值也是false。与shouldRasterize相对,该属性适用于图层内容需要反复重绘的情况, 此时设成true可能会改善性能,比如需要反复绘制大量粒子的粒子发射器图层(可以参考后面的CAEmitterLayer示例)。

    谨记:如果想将已有图层的shouldRasterize或drawsAsynchronously属性设为true,一定要三思而后行,考虑可能造成的影响,对比true与false的性能差异,辨明属性设置是否有积极效果。设置不当甚至会导致性能大幅下降。

    无论如何还是先回到图层演示应用,其中有些控件可以用来调整CALayer的属性:

    调节试试看,感受一下,利用CALayer可以实现怎样的效果。

    注:图层不属于响应链(responder chain),无法像视图一样直接响应触摸和手势,我们在CALayerPlayground中见识过。不过图层有点击测试,后面的 CATransformLayer会提到。你也可以向图层添加自定义动画,CAReplicatorLayer中会出现。

    示例 #2:CAScrollLayer

    CAScrollLayer显示一部分可滚动图层,该图层十分基础,无法直接响应用户的触摸操作,也不能直接检查可滚动图层的边界,故可避免越界无限滚动。

    UIScrollView用的不是CAScrollLayer,而是直接改动图层边界。

    CAScrollLayer的滚动模式可设为水平、垂直或者二维,你也可以用代码命令视图滚动到指定位置:

    1. // In ScrollingView.swift 
    2. import UIKit 
    3.  
    4. class ScrollingView: UIView { 
    5. // 1 
    6. override class func layerClass() -> AnyClass { 
    7. return CAScrollLayer.self 
    8.  
    9. // In CAScrollLayerViewController.swift 
    10. import UIKit 
    11.  
    12. class CAScrollLayerViewController: UIViewController { 
    13. @IBOutlet weak var scrollingView: ScrollingView! 
    14.  
    15. // 2 
    16. var scrollingViewLayer: CAScrollLayer { 
    17. return scrollingView.layer as CAScrollLayer 
    18.  
    19. override func viewDidLoad() { 
    20. super.viewDidLoad() 
    21. // 3 
    22. scrollingViewLayer.scrollMode = kCAScrollBoth 
    23.  
    24. @IBAction func tapRecognized(sender: UITapGestureRecognizer) { 
    25. // 4 
    26. var newPoint = CGPoint(x: 250, y: 250) 
    27. UIView.animateWithDuration(0.3, delay: 0, options: .CurveEaseInOut, animations: { 
    28. [unowned self] in 
    29. self.scrollingViewLayer.scrollToPoint(newPoint) 
    30. }, completion: nil) 
    31.  

    以上代码:

    定义一个继承UIView的类,重写layerClass()返回CAScrollLayer,该方法等同于创建一个新图层作为子图层(CALayer示例中做过)。

    一个用以方便简化访问自定义视图滚动图层的计算属性。

    设滚动模式为二维滚动。

    识别出轻触手势时,让滚动图层在UIView动画中滚到新建的点。(注:scrollToPoint(_:)和scrollToRect(_:)不会自动使用动画效果。)

    案例研究:如果ScrollingView实例包含大于滚动视图边界的图片视图,在运行上述代码并点击视图时结果如下:

    图层演示应用中有可以锁定滚动方向(水平或垂直)的开关。

    以下经验规律用于决定是否使用CAScrollLayer:

    如果想使用轻量级的对象,只需用代码操作滚动:可以考虑CAScrollLayer。

    如果想让用户操作滚动,UIScrollView大概是更好的选择。要了解更多,请参考我们的视频教程。

    如果是滚动大型图片:考虑使用CATiledLayer(见后文)。

    示例 #3:CATextLayer

    CATextLayer能够对普通文本或属性字串进行简单快速的渲染。与UILabel不同,CATextLayer无法指定UIFont,只能使用CTFontRef或CGFontRef。

    像下面这样的代码完全可以掌控文本的字体、字体大小、颜色、对齐、折行(wrap)和截断(truncation)规则,也有动画效果:

    1. // 1 
    2. let textLayer = CATextLayer() 
    3. textLayer.frame = someView.bounds 
    4.  
    5. // 2 
    6. var string = "" 
    7. for _ in 1...20 { 
    8. string += "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce auctor arcu quis velit congue dictum. " 
    9.  
    10. textLayer.string = string 
    11.  
    12. // 3 
    13. let fontName: CFStringRef = "Noteworthy-Light" 
    14. textLayer.font = CTFontCreateWithName(fontName, fontSize, nil) 
    15.  
    16. // 4 
    17. textLayer.foregroundColor = UIColor.darkGrayColor().CGColor 
    18. textLayer.wrapped = true 
    19. textLayer.alignmentMode = kCAAlignmentLeft 
    20. textLayer.contentsScale = UIScreen.mainScreen().scale 
    21. someView.layer.addSublayer(textLayer) 

    以上代码解释如下:

    创建一个CATextLayer实例,令边界与someView相同。

    重复一段文本,创建字符串并赋给文本图层。

    创建一个字体,赋给文本图层。

    将文本图层设为折行、左对齐,你也可以设自然对齐(natural)、右对齐(right)、居中对齐(center)或两端对齐(justified),按屏幕设置contentsScale属性,然后把图层添加到视图结构树。

    不仅是CATextLayer,所有图层类的渲染缩放系数都默认为1。在添加到视图时,图层自身的contentsScale缩放系数会自动调整, 适应当前画面。你需要为手动创建的图层明确指定contentsScale属性,否则默认的缩放系数1会在Retina显示屏上产生部分模糊。

    如果创建的文本图层添加到了方形的someView,效果会像这样:

     

    你可以设置截断(Truncation)属性,生效时被截断的部分文本会由省略号代替显示。默认设定为无截断,位置可设为开头、末尾或中间截断:

    图层演示应用中,你可以随心所欲地修改很多CATextLayer属性:

    示例 #4:AVPlayerLayer

    AVPlayerLayer是建立在AVFoundation基础上的实用图层,持有一个AVPlayer,用来播放音视频媒体文件(AVPlayerItems),举例如下:

    1. override func viewDidLoad() { 
    2. super.viewDidLoad() 
    3. // 1 
    4. let playerLayer = AVPlayerLayer() 
    5. playerLayer.frame = someView.bounds 
    6.  
    7. // 2 
    8. let url = NSBundle.mainBundle().URLForResource("someVideo", withExtension: "m4v") 
    9. let player = AVPlayer(URL: url) 
    10.  
    11. // 3 
    12. player.actionAtItemEnd = .None 
    13. playerLayer.player = player 
    14. someView.layer.addSublayer(playerLayer) 
    15.  
    16. // 4 
    17. NSNotificationCenter.defaultCenter().addObserver(self, selector: "playerDidReachEndNotificationHandler:", name: "AVPlayerItemDidPlayToEndTimeNotification", object: player.currentItem) 
    18.  
    19. deinit { 
    20. NSNotificationCenter.defaultCenter().removeObserver(self) 
    21.  
    22. // 5 
    23. @IBAction func playButtonTapped(sender: UIButton) { 
    24. if playButton.titleLabel?.text == "Play" { 
    25. player.play() 
    26. playButton.setTitle("Pause", forState: .Normal) 
    27. else { 
    28. player.pause() 
    29. playButton.setTitle("Play", forState: .Normal) 
    30.  
    31. updatePlayButtonTitle() 
    32. updateRateSegmentedControl() 
    33.  
    34. // 6 
    35. func playerDidReachEndNotificationHandler(notification: NSNotification) { 
    36. let playerItem = notification.object as AVPlayerItem 
    37. playerItem.seekToTime(kCMTimeZero) 

    上述代码解释:

    新建一个播放器图层,设置框架。

    使用AV asset资源创建一个播放器。

    告知命令播放器在播放完成后停止。其他选项还有暂停或自动播放下一个媒体资源。

    注册AVPlayer通知,在一个文件播放完毕后发送通知,并在析构函数中删除作为观察者的控制器。

    点击播放按钮时,触发控件播放AV asset并设置按钮文字。

    注意这只是个入门示例,在实际项目中往往不会采用文字按钮控制播放。

    AVPlayerLayer和其中创建的AVPlayer会像这样显示为AVPlayerItem实例的第一帧:

    AVPlayerLayer还有一些附加属性:

    videoGravity设置视频显示的缩放行为。

    readyForDisplay检测是否准备好播放视频。

    另一方面,AVPlayer也有不少附加属性和方法,有一个值得注意的是rate属性,对于0到1之间的播放速率,0代表暂停,1代表常速播放(1x)。

    不过rate属性的设置是与播放行为联动的,也就是说调用pause()方法和把rate设为0是等价的,调用play()与把rate设为1也一样。

    那快进、慢动作和反向播放呢?交给AVPlayerLayer把。rate大于1时会令播放器以相应倍速进行播放,例如rate设为2就是二倍速。

    如你所想,rate为负时会让播放器以相应倍速反向播放。

    然而,在以非常规速率播放之前,AVPlayerItem上会调用适当方法,验证是否能够以相应速率进行播放:

    canPlayFastForward()对应大于1

    canPlaySlowForward()对应0到1之间

    canPlayReverse()对应-1

    canPlaySlowReverse()对应-1到0之间

    canPlayFastReverse()对应小于-1

    绝大多数视频都支持以不同速率正向播放,可以反向播放的视频相对少一些。演示应用也包含了播放控件:

    示例 #5:CAGradientLayer

    CAGradientLayer简化了混合两种或更多颜色的工作,尤其适用于背景。要配置渐变色,你需要分配一个CGColor数组,以及标识渐变图层起止点的startPoint和endPoint。

    注意:startPoint和endPoint并不是明确的点,而是用单位坐标空间定义,在绘制时映射到图层边界。也就是说x值为1表示点在图层右边缘,y值为1表示点在图层下边缘。

    CAGradientLayer包含type属性,虽说该属性只有kCAGradientLayerAxial一个选择,由数组中的各颜色产生线性过渡渐变。

    具体含义是渐变过渡沿startPoint到endPoint的向量A方向产生,设B与A垂直,则各条B平行线上的所有点颜色相同。

    此外,locations属性可以使用一个数组(元素取值范围0到1),指定渐变图层参照colors顺序取用下一个过渡点颜色的位置。

    未设定时默认会平均分配过渡点。一旦设定就必须与colors的数量保持一致,否则会出错。 :[

    下面是创建渐变图层的例子:

    1. let gradientLayer = CAGradientLayer() 
    2. gradientLayer.frame = someView.bounds 
    3. gradientLayer.colors = [cgColorForRed(209.0, green: 0.0, blue: 0.0), 
    4. cgColorForRed(255.0, green: 102.0, blue: 34.0), 
    5. cgColorForRed(255.0, green: 218.0, blue: 33.0), 
    6. cgColorForRed(51.0, green: 221.0, blue: 0.0), 
    7. cgColorForRed(17.0, green: 51.0, blue: 204.0), 
    8. cgColorForRed(34.0, green: 0.0, blue: 102.0), 
    9. cgColorForRed(51.0, green: 0.0, blue: 68.0)] 
    10. gradientLayer.startPoint = CGPoint(x: 0, y: 0) 
    11. gradientLayer.endPoint = CGPoint(x: 0, y: 1) 
    12. someView.layer.addSublayer(gradientLayer) 



    func cgColorForRed(red: CGFloat, green: CGFloat, blue: CGFloat) -> AnyObject {
    return UIColor(red: red/255.0, green: green/255.0, blue: blue/255.0, alpha: 1.0).CGColor as AnyObject
    }

    上述代码创建一个渐变图层,框架设为someView边界,指定颜色数组,设置起止点,添加图层到视图结构树。效果如下:

    五彩缤纷,姹紫嫣红!

    图层演示应用中,你可以随意修改起止点、颜色和过渡点:

    示例 #6:CAReplicatorLayer

    CAReplicatorLayer能够以特定次数复制图层,可以用来创建一些很棒的效果。

    每个图层复件的颜色和位置都可以改动,而且可以在总复制图层之后延迟绘制,营造一种动画效果。还可以利用深度,创造三维效果。举个例子

    1. // 1 
    2. let replicatorLayer = CAReplicatorLayer() 
    3. replicatorLayer.frame = someView.bounds 
    4.  
    5. // 2 
    6. replicatorLayer.instanceCount = 30 
    7. replicatorLayer.instanceDelay = CFTimeInterval(1 / 30.0) 
    8. replicatorLayer.preservesDepth = false 
    9. replicatorLayer.instanceColor = UIColor.whiteColor().CGColor 
    10.  
    11. // 3 
    12. replicatorLayer.instanceRedOffset = 0.0 
    13. replicatorLayer.instanceGreenOffset = -0.5 
    14. replicatorLayer.instanceBlueOffset = -0.5 
    15. replicatorLayer.instanceAlphaOffset = 0.0 
    16.  
    17. // 4 
    18. let angle = Float(M_PI * 2.0) / 30 
    19. replicatorLayer.instanceTransform = CATransform3DMakeRotation(CGFloat(angle), 0.0, 0.0, 1.0) 
    20. someView.layer.addSublayer(replicatorLayer) 
    21.  
    22. // 5 
    23. let instanceLayer = CALayer() 
    24. let layerWidth: CGFloat = 10.0 
    25. let midX = CGRectGetMidX(someView.bounds) - layerWidth / 2.0 
    26. instanceLayer.frame = CGRect(x: midX, y: 0.0,  layerWidth, height: layerWidth * 3.0) 
    27. instanceLayer.backgroundColor = UIColor.whiteColor().CGColor 
    28. replicatorLayer.addSublayer(instanceLayer) 
    29.  
    30. // 6 
    31. let fadeAnimation = CABasicAnimation(keyPath: "opacity") 
    32. fadeAnimation.fromValue = 1.0 
    33. fadeAnimation.toValue = 0.0 
    34. fadeAnimation.duration = 
    35. fadeAnimation.repeatCount = Float(Int.max) 
    36.  
    37. // 7 
    38. instanceLayer.opacity = 0.0 
    39. instanceLayer.addAnimation(fadeAnimation, forKey: "FadeAnimation") 

    以上代码:

    创建一个CAReplicatorLayer实例,设框架为someView边界。

    设复制图层数instanceCount和绘制延迟,设图层为2D(preservesDepth = false),实例颜色为白色。

    为陆续的实例复件设置RGB颜色偏差值(默认为0,即所有复件保持颜色不变),不过这里实例初始颜色为白色,即RGB都为1.0,所以偏差值设红色为0,绿色和蓝色为相同负数会使其逐渐现出红色,alpha透明度偏差值的变化也与此类似,针对陆续的实例复件。

    创建旋转变换,使得实例复件按一个圆排列。

    创建供复制图层使用的实例图层,设置框架,使第一个实例在someView边界顶端水平中心处绘制,另外设置实例颜色,把实例图层添加到复制图层。

    创建一个透明度由1(不透明)过渡为0(透明)的淡出动画。

    设实例图层透明度为0,使得每个实例在绘制和改变颜色与alpha前保持透明。

    这段代码会实现这样的东西:

    图层演示应用中,你可以改动这些属性:

    示例 #7:CATiledLayer

    CATiledLayer以图块(tile)为单位异步绘制图层内容,对超大尺寸图片或者只能在视图中显示一小部分的内容效果拔群,因为不用把内容完全载入内存就可以看到内容。

    处理绘制有几种方法,一种是重写UIView,使用CATiledLayer绘制图块填充视图背景,如下:

    1. // In ViewController.swift 
    2. import UIKit 
    3.  
    4. class ViewController: UIViewController { 
    5.  
    6. // 1 
    7. @IBOutlet weak var tiledBackgroundView: TiledBackgroundView! 
    8.  
    9.  
    10. // In TiledBackgroundView.swift 
    11. import UIKit 
    12.  
    13. class TiledBackgroundView: UIView { 
    14.  
    15. let sideLength = CGFloat(50.0) 
    16.  
    17. // 2 
    18. override class func layerClass() -> AnyClass { 
    19. return CATiledLayer.self 
    20.  
    21. // 3 
    22. required init(coder aDecoder: NSCoder) { 
    23. super.init(coder: aDecoder) 
    24. srand48(Int(NSDate().timeIntervalSince1970)) 
    25. let layer = self.layer as CATiledLayer 
    26. let scale = UIScreen.mainScreen().scale 
    27. layer.contentsScale = scale 
    28. layer.tileSize = CGSize( sideLength * scale, height: sideLength * scale) 
    29.  
    30. // 4 
    31. override func drawRect(rect: CGRect) { 
    32. let context = UIGraphicsGetCurrentContext() 
    33. var red = CGFloat(drand48()) 
    34. var green = CGFloat(drand48()) 
    35. var blue = CGFloat(drand48()) 
    36. CGContextSetRGBFillColor(context, red, green, blue, 1.0) 
    37. CGContextFillRect(context, rect) 
    38.  

    代码解释:

    tiledBackgroundView位于 (150, 150) ,宽高均为300。

    重写layerClass(),令该视图创建的图层实例为CATiledLayer。

    设置rand48()的随机数种子,用于在drawRect()中生成随机颜色。CATiledLayer类型转换,缩放图层内容,设置图块尺寸,适应屏幕。

    重写drawRect(),以随机色块填充视图。

    代码绘制6×6随机色块方格,最终效果如下:

    图层演示应用中除此之外还可以在图层背景上绘制轨迹:

    在视图中放大时,上述截图中的星星图案会变得模糊:

    产生模糊的根源是图层的细节层次(level of detail,简称LOD),CATiledLayer有两个相关属性:levelsOfDetail和levelsOfDetailBias。

    levelsOfDetail顾名思义,指图层维护的LOD数目,默认值为1,每进一级会对前一级分辨率的一半进行缓存,图层的levelsOfDetail最大值,也就是最底层细节,对应至少一个像素点。

    而levelsOfDetailBias指的是该图层缓存的放大LOD数目,默认为0,即不会额外缓存放大层次,每进一级会对前一级两倍分辨率进行缓存。

    例如,设上述分块图层的levelsOfDetailBias为5会缓存2x、4x、8x、16x和32x的放大层次,放大的图层效果如下:

     

    不错吧?别着急,还没讲完呢。

    CATiledLayer裁刀,买不了吃亏,买不了上当,只要998…(译注:此处内容稍作本地化处理,原文玩的是1978年美国Ginsu刀具的梗,堪称询价型电视购物广告的万恶之源。) :]

    开个玩笑。CATiledLayer还有一个更实用的功能:异步绘制图块,比如在滚动视图中显示一张超大图片。

    在用户滚动画面时,要让分块图层知道哪些图块需要绘制,写代码在所难免,不过换来性能提升也值了。

    图层演示应用的UIImage+TileCutter.swift中包含一个UIImage扩展,教程编纂组成员Nick Lockwood在著作iOS Core Animation: Advanced Techniques的一个终端应用程序中利用了这段代码。

    代码的职责是把原图片拆分成指定尺寸的方块,按行列位置命名图块,比如第三行第七列的图块windingRoad62.png(索引从零开始)。

     

    有了这些图块,我们可以自定义一个UIView子类,绘制分块图层:

    1. mport UIKit 
    2.  
    3. class TilingViewForImage: UIView { 
    4.  
    5. // 1 
    6. let sideLength = CGFloat(640.0) 
    7. let fileName = "windingRoad" 
    8. let cachesPath = NSSearchPathForDirectoriesInDomains(.CachesDirectory, .UserDomainMask, true)[0] as String 
    9.  
    10. // 2 
    11. override class func layerClass() -> AnyClass { 
    12. return CATiledLayer.self 
    13.  
    14. // 3 
    15. required init(coder aDecoder: NSCoder) { 
    16. super.init(coder: aDecoder) 
    17. let layer = self.layer as CATiledLayer 
    18. layer.tileSize = CGSize( sideLength, height: sideLength) 
    19.  
    20. // 4 
    21. override func drawRect(rect: CGRect) { 
    22. let firstColumn = Int(CGRectGetMinX(rect) / sideLength) 
    23. let lastColumn = Int(CGRectGetMaxX(rect) / sideLength) 
    24. let firstRow = Int(CGRectGetMinY(rect) / sideLength) 
    25. let lastRow = Int(CGRectGetMaxY(rect) / sideLength) 
    26.  
    27. for row in firstRow...lastRow { 
    28. for column in firstColumn...lastColumn { 
    29. if let tile = imageForTileAtColumn(column, row: row) { 
    30. let x = sideLength * CGFloat(column) 
    31. let y = sideLength * CGFloat(row) 
    32. let point = CGPoint(x: x, y: y) 
    33. let size = CGSize( sideLength, height: sideLength) 
    34. var tileRect = CGRect(origin: point, size: size) 
    35. tileRect = CGRectIntersection(bounds, tileRect) 
    36. tile.drawInRect(tileRect) 
    37.  
    38. func imageForTileAtColumn(column: Int, row: Int) -> UIImage? { 
    39. let filePath = "(cachesPath)/(fileName)_(column)_(row)" 
    40. return UIImage(contentsOfFile: filePath) 
    41.  

    以上代码:

    创建属性,分别是图块边长、原图文件名、供TileCutter扩展保存图块的缓存文件夹路径。

    重写layerClass()返回CATiledLayer。

    实现init(_:),把视图的图层转换为分块图层,设置图块大小。注意此处不必设置contentsScale适配屏幕,因为是直接修改视图自身的图层,而不是手动创建子图层。

    重写drawRect(),按行列绘制各个图块。

    像这样,原图大小的自定义视图就可以塞进一个滚动视图:

    多亏CATiledLayer,滚动5120 x 3200的大图也会这般顺滑:

    如你所见,快速滚动时绘制图块的过程还是很明显,你可以利用更小的分块(上述例子中分块为640 x 640),或者自己创建一个CATiledLayer子类,重写fadeDuration()返回0:

    1. class TiledLayer: CATiledLayer { 
    2.  
    3. override class func fadeDuration() -> CFTimeInterval { 
    4. return 0.0 
    5.  

    示例 #8:CAShapeLayer

    CAShapeLayer利用可缩放的矢量路径进行绘制,绘制速度比使用图片快很多,还有个好处是不用分别提供常规、@2x和@3x版本的图片,好用。

    另外还有各种属性,让你可以自定线粗、颜色、虚实、线条接合方式、闭合线条是否形成闭合区域,还有闭合区域要填充何种颜色等。举例如下

    1. import UIKit 
    2.    
    3. class ViewController: UIViewController { 
    4.    
    5.   @IBOutlet weak var someView: UIView! 
    6.    
    7.   // 1 
    8.   let rwColor = UIColor(red: 11/255.0, green: 86/255.0, blue: 14/255.0, alpha: 1.0) 
    9.   let rwPath = UIBezierPath() 
    10.   let rwLayer = CAShapeLayer() 
    11.    
    12.   // 2 
    13.   func setUpRWPath() { 
    14.     rwPath.moveToPoint(CGPointMake(0.22, 124.79)) 
    15.     rwPath.addLineToPoint(CGPointMake(0.22, 249.57)) 
    16.     rwPath.addLineToPoint(CGPointMake(124.89, 249.57)) 
    17.     rwPath.addLineToPoint(CGPointMake(249.57, 249.57)) 
    18.     rwPath.addLineToPoint(CGPointMake(249.57, 143.79)) 
    19.     rwPath.addCurveToPoint(CGPointMake(249.37, 38.25), controlPoint1: CGPointMake(249.57, 85.64), controlPoint2: CGPointMake(249.47, 38.15)) 
    20.     rwPath.addCurveToPoint(CGPointMake(206.47, 112.47), controlPoint1: CGPointMake(249.27, 38.35), controlPoint2: CGPointMake(229.94, 71.76)) 
    21.     rwPath.addCurveToPoint(CGPointMake(163.46, 186.84), controlPoint1: CGPointMake(182.99, 153.19), controlPoint2: CGPointMake(163.61, 186.65)) 
    22.     rwPath.addCurveToPoint(CGPointMake(146.17, 156.99), controlPoint1: CGPointMake(163.27, 187.03), controlPoint2: CGPointMake(155.48, 173.59)) 
    23.     rwPath.addCurveToPoint(CGPointMake(128.79, 127.08), controlPoint1: CGPointMake(136.82, 140.43), controlPoint2: CGPointMake(129.03, 126.94)) 
    24.     rwPath.addCurveToPoint(CGPointMake(109.31, 157.77), controlPoint1: CGPointMake(128.59, 127.18), controlPoint2: CGPointMake(119.83, 141.01)) 
    25.     rwPath.addCurveToPoint(CGPointMake(89.83, 187.86), controlPoint1: CGPointMake(98.79, 174.52), controlPoint2: CGPointMake(90.02, 188.06)) 
    26.     rwPath.addCurveToPoint(CGPointMake(56.52, 108.28), controlPoint1: CGPointMake(89.24, 187.23), controlPoint2: CGPointMake(56.56, 109.11)) 
    27.     rwPath.addCurveToPoint(CGPointMake(64.02, 102.25), controlPoint1: CGPointMake(56.47, 107.75), controlPoint2: CGPointMake(59.24, 105.56)) 
    28.     rwPath.addCurveToPoint(CGPointMake(101.42, 67.57), controlPoint1: CGPointMake(81.99, 89.78), controlPoint2: CGPointMake(93.92, 78.72)) 
    29.     rwPath.addCurveToPoint(CGPointMake(108.38, 30.65), controlPoint1: CGPointMake(110.28, 54.47), controlPoint2: CGPointMake(113.01, 39.96)) 
    30.     rwPath.addCurveToPoint(CGPointMake(10.35, 0.41), controlPoint1: CGPointMake(99.66, 13.17), controlPoint2: CGPointMake(64.11, 2.16)) 
    31.     rwPath.addLineToPoint(CGPointMake(0.22, 0.07)) 
    32.     rwPath.addLineToPoint(CGPointMake(0.22, 124.79)) 
    33.     rwPath.closePath() 
    34.   } 
    35.    
    36.   // 3 
    37.   func setUpRWLayer() { 
    38.     rwLayer.path = rwPath.CGPath 
    39.     rwLayer.fillColor = rwColor.CGColor 
    40.     rwLayer.fillRule = kCAFillRuleNonZero 
    41.     rwLayer.lineCap = kCALineCapButt 
    42.     rwLayer.lineDashPattern = nil 
    43.     rwLayer.lineDashPhase = 0.0 
    44.     rwLayer.lineJoin = kCALineJoinMiter 
    45.     rwLayer.lineWidth = 1.0 
    46.     rwLayer.miterLimit = 10.0 
    47.     rwLayer.strokeColor = rwColor.CGColor 
    48.   } 
    49.    
    50.   override func viewDidLoad() { 
    51.     super.viewDidLoad() 
    52.    
    53.     // 4 
    54.     setUpRWPath() 
    55.     setUpRWLayer() 
    56.     someView.layer.addSublayer(rwLayer) 
    57.   } 
    58.    
    59. }

    代码解释:

    创建颜色、路径、图形图层对象。

    绘制图形图层路径。如果不喜欢编写生硬的绘图代码的话,你可以尝试PaintCode这款软件,可以利用简便的工具进行可视化绘制,支持导入现有的矢量图(SVG)和Photoshop(PSD)文件,并自动生成代码。

    设置图形图层。路径设为第二步中绘制的CGPath路径,填充色设为第一步中创建的CGColor颜色,填充规则设为非零(non-zero),即默认填充规则。

    填充规则共有两种,另一种是奇偶(even-odd)。不过示例代码中的图形没有相交路径,两种填充规则的结果并无差异。

    非零规则记从左到右的路径为+1,从右到左的路径为-1,累加所有路径值,若总和大于零,则填充路径围成的图形。

    从结果上来讲,非零规则会填充图形内部所有的点。

    奇偶规则计算围成图形的路径交叉数,若结果为奇数则填充。这样讲有些晦涩,还是有图有真相:

    右图围成中间五边形的路径交叉数为偶数,故中间没有填充,而围成每个三角的路径交叉数为奇数,故三角部分填充颜色。

    调用路径绘制和图层设置代码,并把图层添加到视图结构树。

    上述代码绘制raywenderlich.com的图标:

    顺便看看使用PaintCode的效果图:

    图层演示应用中,你可以随意修改很多CAShapeLayer属性:

    注:我们先跳过演示应用中的下一个示例,因为CAEAGLLayer多少显得有些过时了,iOS 8 Metal框架有更先进的CAMetalLayer。在此推荐iOS 8 Metal入门教程。

    示例 #9:CATransformLayer

    CATransformLayer不像其他图层类一样把子图层结构平面化,故适宜绘制3D结构。变换图层本质上是一个图层容器,每个子图层都可以应用自己的透明度和空间变换,而其他渲染图层属性(如边宽、颜色)会被忽略。

    变换图层本身不支持点击测试,因为无法直接在触摸点和平面坐标空间建立映射,不过其中的子图层可以响应点击测试,例如:

    1. import UIKit 
    2.  
    3. class ViewController: UIViewController { 
    4.  
    5. @IBOutlet weak var someView: UIView! 
    6.  
    7. // 1 
    8. let sideLength = CGFloat(160.0) 
    9. var redColor = UIColor.redColor() 
    10. var orangeColor = UIColor.orangeColor() 
    11. var yellowColor = UIColor.yellowColor() 
    12. var greenColor = UIColor.greenColor() 
    13. var blueColor = UIColor.blueColor() 
    14. var purpleColor = UIColor.purpleColor() 
    15. var transformLayer = CATransformLayer() 
    16.  
    17. // 2 
    18. func setUpTransformLayer() { 
    19. var layer = sideLayerWithColor(redColor) 
    20. transformLayer.addSublayer(layer) 
    21.  
    22. layer = sideLayerWithColor(orangeColor) 
    23. var transform = CATransform3DMakeTranslation(sideLength / 2.0, 0.0, sideLength / -2.0) 
    24. transform = CATransform3DRotate(transform, degreesToRadians(90.0), 0.0, 1.0, 0.0) 
    25. layer.transform = transform 
    26. transformLayer.addSublayer(layer) 
    27.  
    28. layer = sideLayerWithColor(yellowColor) 
    29. layer.transform = CATransform3DMakeTranslation(0.0, 0.0, -sideLength) 
    30. transformLayer.addSublayer(layer) 
    31.  
    32. layer = sideLayerWithColor(greenColor) 
    33. transform = CATransform3DMakeTranslation(sideLength / -2.0, 0.0, sideLength / -2.0) 
    34. transform = CATransform3DRotate(transform, degreesToRadians(90.0), 0.0, 1.0, 0.0) 
    35. layer.transform = transform 
    36. transformLayer.addSublayer(layer) 
    37.  
    38. layer = sideLayerWithColor(blueColor) 
    39. transform = CATransform3DMakeTranslation(0.0, sideLength / -2.0, sideLength / -2.0) 
    40. transform = CATransform3DRotate(transform, degreesToRadians(90.0), 1.0, 0.0, 0.0) 
    41. layer.transform = transform 
    42. transformLayer.addSublayer(layer) 
    43.  
    44. layer = sideLayerWithColor(purpleColor) 
    45. transform = CATransform3DMakeTranslation(0.0, sideLength / 2.0, sideLength / -2.0) 
    46. transform = CATransform3DRotate(transform, degreesToRadians(90.0), 1.0, 0.0, 0.0) 
    47. layer.transform = transform 
    48. transformLayer.addSublayer(layer) 
    49.  
    50. transformLayer.anchorPointZ = sideLength / -2.0 
    51. applyRotationForXOffset(16.0, yOffset: 16.0) 
    52.  
    53. // 3 
    54. func sideLayerWithColor(color: UIColor) -> CALayer { 
    55. let layer = CALayer() 
    56. layer.frame = CGRect(origin: CGPointZero, size: CGSize( sideLength, height: sideLength)) 
    57. layer.position = CGPoint(x: CGRectGetMidX(someView.bounds), y: CGRectGetMidY(someView.bounds)) 
    58. layer.backgroundColor = color.CGColor 
    59. return layer 
    60.  
    61. func degreesToRadians(degrees: Double) -> CGFloat { 
    62. return CGFloat(degrees * M_PI / 180.0) 
    63.  
    64. // 4 
    65. func applyRotationForXOffset(xOffset: Double, yOffset: Double) { 
    66. let totalOffset = sqrt(xOffset * xOffset + yOffset * yOffset) 
    67. let totalRotation = CGFloat(totalOffset * M_PI / 180.0) 
    68. let xRotationalFactor = CGFloat(totalOffset) / totalRotation 
    69. let yRotationalFactor = CGFloat(totalOffset) / totalRotation 
    70. let currentTransform = CATransform3DTranslate(transformLayer.sublayerTransform, 0.0, 0.0, 0.0) 
    71. let rotationTransform = CATransform3DRotate(transformLayer.sublayerTransform, totalRotation, 
    72. xRotationalFactor * currentTransform.m12 - yRotationalFactor * currentTransform.m11, 
    73. xRotationalFactor * currentTransform.m22 - yRotationalFactor * currentTransform.m21, 
    74. xRotationalFactor * currentTransform.m32 - yRotationalFactor * currentTransform.m31) 
    75. transformLayer.sublayerTransform = rotationTransform 
    76.  
    77. // 5 
    78. override func touchesBegan(touches: NSSet, withEvent event: UIEvent) { 
    79. if let location = touches.anyObject()?.locationInView(someView) { 
    80. for layer in transformLayer.sublayers { 
    81. if let hitLayer = layer.hitTest(location) { 
    82. println("Transform layer tapped!") 
    83. break 
    84.  
    85. override func viewDidLoad() { 
    86. super.viewDidLoad() 
    87.  
    88. // 6 
    89. setUpTransformLayer() 
    90. someView.layer.addSublayer(transformLayer) 
    91.  

    上述代码解释:

    创建属性,分别为立方体的边长、每个面的颜色,还有一个变换图层。

    创建六个面,旋转后添加到变换图层,构成立方体,然后设置变换图层的z轴锚点,旋转立方体,将其添加到视图结构树。

    辅助代码,用来创建指定颜色的面,还有角度和弧度的转换。在变换代码中利用弧度转换函数在某种程度上可以增加代码可读性。 :]

    基于指定xy偏移的旋转,注意变换应用对象设为sublayerTransform,即变换图层的子图层。

    监听触摸,遍历变换图层的子图层,对每个图层进行点击测试,一旦成功相应立即跳出循环,不用继续遍历。

    设置变换图层,添加到视图结构树。

    注:currentTransform.m##是啥?问得好,是CATransform3D属性,代表矩阵元素。想学习如上代码中的矩阵变换,请参考RW教程组成员Rich Turton的三维变换娱乐教学,还有Mark Pospesel的初识矩阵项目。

    在250 x 250的someView视图中运行上述代码结果如下:

    再试试点击立方体的任意位置,控制台会输出“Transform layer tapped!”信息。

    图层演示应用中可以调整透明度,此外Bill Dudney轨迹球工具, Swift移植版可以基于简单的用户手势应用三维变换。

    示例 #10:CAEmitterLayer

    CAEmitterLayer渲染的动画粒子是CAEmitterCell实例。CAEmitterLayer和CAEmitterCell都包含可调整渲染频率、大小、形状、颜色、速率以及生命周期的属性。示例如下:

    1. import UIKit 
    2.  
    3. class ViewController: UIViewController { 
    4.  
    5. // 1 
    6. let emitterLayer = CAEmitterLayer() 
    7. let emitterCell = CAEmitterCell() 
    8.  
    9. // 2 
    10. func setUpEmitterLayer() { 
    11. emitterLayer.frame = view.bounds 
    12. emitterLayer.seed = UInt32(NSDate().timeIntervalSince1970) 
    13. emitterLayer.renderMode = kCAEmitterLayerAdditive 
    14. emitterLayer.drawsAsynchronously = true 
    15. setEmitterPosition() 
    16.  
    17. // 3 
    18. func setUpEmitterCell() { 
    19. emitterCell.contents = UIImage(named: "smallStar")?.CGImage 
    20.  
    21. emitterCell.velocity = 50.0 
    22. emitterCell.velocityRange = 500.0 
    23.  
    24. emitterCell.color = UIColor.blackColor().CGColor 
    25. emitterCell.redRange = 1.0 
    26. emitterCell.greenRange = 1.0 
    27. emitterCell.blueRange = 1.0 
    28. emitterCell.alphaRange = 0.0 
    29. emitterCell.redSpeed = 0.0 
    30. emitterCell.greenSpeed = 0.0 
    31. emitterCell.blueSpeed = 0.0 
    32. emitterCell.alphaSpeed = -0.5 
    33.  
    34. let zeroDegreesInRadians = degreesToRadians(0.0) 
    35. emitterCell.spin = degreesToRadians(130.0) 
    36. emitterCell.spinRange = zeroDegreesInRadians 
    37. emitterCell.emissionRange = degreesToRadians(360.0) 
    38.  
    39. emitterCell.lifetime = 1.0 
    40. emitterCell.birthRate = 250.0 
    41. emitterCell.xAcceleration = -800.0 
    42. emitterCell.yAcceleration = 1000.0 
    43.  
    44. // 4 
    45. func setEmitterPosition() { 
    46. emitterLayer.emitterPosition = CGPoint(x: CGRectGetMidX(view.bounds), y: CGRectGetMidY(view.bounds)) 
    47.  
    48. func degreesToRadians(degrees: Double) -> CGFloat { 
    49. return CGFloat(degrees * M_PI / 180.0) 
    50.  
    51. override func viewDidLoad() { 
    52. super.viewDidLoad() 
    53.  
    54. // 5 
    55. setUpEmitterLayer() 
    56. setUpEmitterCell() 
    57. emitterLayer.emitterCells = [emitterCell] 
    58. view.layer.addSublayer(emitterLayer) 
    59.  
    60. // 6 
    61. override func traitCollectionDidChange(previousTraitCollection: UITraitCollection?) { 
    62. setEmitterPosition() 
    63.  

    以上代码解析:

    1.创建粒子发射器图层和粒子胞(Creates an emitter layer and cell.)。

    2.按照下方步骤设置粒子发射器图层:

    为随机数生成器提供种子,随机调整粒子胞的某些属性,如速度。

    在图层背景色和边界之上按renderMode指定的顺序渲染粒子胞。

    注:渲染模式默认为无序(unordered),其他模式包括旧粒子优先(oldest first),新粒子优先(oldest last),按z轴位置从后至前(back to front)还有叠加式渲染(additive)。

    由于粒子发射器需要反复重绘大量粒子胞,设drawsAsynchronously为true会提升性能。

    然后借助第四条中会提到的辅助方法设置发射器位置,这个例子有助于理解把drawsAsynchronously设为true为何能够提升性能和动画流畅度。

    3.这段代码设了不少东西。

    配置粒子胞,设内容为图片(图片在图层演示项目中)。

    指定初速及其变化量范围(velocityRange),发射器图层利用上面提到的随机数种子创建随机数生成器,在范围内产生随机值(初值+/-变化量范围),其他以“Range”结尾的相关属性的随机化规则类似。

    设颜色为黑色,使自变色(variance)与默认的白色形成对比,白色形成的粒子亮度过高。

    利用随机化范围设置颜色,指定自变色范围,颜色速度值表示粒子胞生命周期内颜色变化快慢。

    接下来这几行代码指定粒子胞分布范围,一个全圆锥。设置粒子胞转速和发射范围,发射范围emissionRange属性的弧度值决定粒子胞分布空间。

    设粒子胞生命周期为1秒,默认值为0,表示粒子胞不会出现。birthRate也类似,以秒为单位,默认值为0,为使粒子胞显示出来,必须设成正数。

    最后设xy加速度,这些值会影响已发射粒子的视角。

    4.把角度转换成弧度的辅助方法,还有设置粒子胞位置为视图中点。

    5.设置发射器图层和粒子胞,把粒子胞添加到图层,然后把图层添加到视图结构树。

    6.iOS 8的新方法,处理当前设备形态集(trait collection)的变化,比如设备旋转。不熟悉形态集的话可以参阅iOS 8教程。

    总算说完了!信息量很大,但相信各位聪明的读者可以高效吸收。

    上述代码运行效果如下:

    图层演示应用中,你可以随意调节很多属性:

    何去何从?

    恭喜,看完十则示例和各种图层子类,CALayer之旅至此告一段落。

    但现在才刚刚开始!新建一个项目,或者打开已有项目,尝试利用图层提升性能或营造酷炫效果!实践出真知。

  • 相关阅读:
    2019-2020-2 20175302闫君廷《网络对抗技术》Exp1 PC平台逆向破解
    2020-4 网络对抗技术 20175120 exp8 web基础
    2020-3 网络对抗技术 20175120 exp7 网络欺诈防范
    2020-2 网络对抗技术 20175120 exp6 MSF基础应用
    2020-2 网络对抗技术 20175120 exp5 信息搜集与漏洞扫描
    2020-2 网络对抗技术 20175120 exp4 恶意代码分析
    2020-2 网络对抗技术 20175120 exp3 免杀原理与实践
    2020-2 网络对抗技术 20175120 exp2 后门原理与实践
    2020-2 网络对抗技术 20175120 exp1 逆向及Bof基础实践
    2018-2019-2 20175120 实验五《Java网络编程》实验报告
  • 原文地址:https://www.cnblogs.com/Free-Thinker/p/4962289.html
Copyright © 2020-2023  润新知