CALayer之旅(CALayer Tutorial: Getting Started)

## CALayer之旅(CALayer Tutorial: Getting Started)
Note:如果你没有上面我们所说的那些基础,没关系,我们有不少关于CALayer的教程和书籍,比如:Learn to Code iOS Apps with Swift和 The iOS Apprentice。
### 开篇(Getting Started) 要想学习layer,最简单的方法就是在实践中去使用它,所以,让我们从头开始创建一个简单的项目来玩转layer。 备好纸巾了么~~~ 好的,首先打开Xcode。 1 .在菜单栏选择File -> New -> Project 2 .依次选择 iOS -> Application -> Single View Application 3 .点击下一步,输入CALayerPlayground作为项目的名称,依次输入组织名称和标识符。 4 .选择项目语言为Swift,设备选项选择通用(Universal)。 5 .不要选择Core Data项,然后单击Next。 6 .为你的项目设置路径(我通常会选择用户目录下的Source文件夹),然后点击Create。 Okay!现在你的项目已经创建完成,接下来我们的任务是创建一个View。 7 .在Xcode的导航栏中,选中Main.storyboard。 8 .依次选择Xcode菜单栏中的 View -> Assistant Editor -> Show Assistant Editor,如果Object Library没有显示则依次选择View -> Utilities -> Show Object Library 9 .并且选择Xcode菜单栏的Editor -> Canvas -> Show Bounds Rectangles,这样你就可以看到将要添加到场景中的视图的边界轮廓, 10 .从Object library拖一个View到View Controller Sence上,选中它,在Size inspector中设置它的x,y值为150,宽高为300. 11 .使View处于选中状态,单击自动布局工具栏的Align按钮(故事板右下角),勾选Horizontal Center in Container和Vertical Center in Container,并且设置它们的值为0,单击Add 2 Constraints。 12 .点击Pin按钮,检查并确保它们的宽高都是300,单击Add 2 Constraints。 最后, 连线你所创建的View到和View所在场景相关联的ViewController.swift文件中,并且置于viewDidLoad()方法的上面。在弹出框中输入view的名字viewForLayer,这时你的Xcode如下图所示:

点击Connect创建outlet

替换ViewController.swift中的内容如下:

import UIKit

class ViewController: UIViewController {

  @IBOutlet weak var viewForLayer: UIView!

  var l: CALayer {
    return viewForLayer.layer
  }

  override func viewDidLoad() {
    super.viewDidLoad()
    setUpLayer()
  }

  func setUpLayer() {
    l.backgroundColor = UIColor.blueColor().CGColor
    l.borderWidth = 100.0
    l.borderColor = UIColor.redColor().CGColor
    l.shadowOpacity = 0.7
    l.shadowRadius = 10.0
  }

}
正如上面提到的,iOS APP中的每一个view都有一个layer和它相关联,你可以通过youView.layer来获取到它,这段代码所做的第一件事就是创建了一个l的变量(小写的L)用于接收viewForLayer的layer,这样做可以在后面方便的引用viewForLayer的layer。 代码中调用setUpLayer为l设置了几个属性:阴影,蓝色的背景和红色的边框颜色,一会儿我们会在setUpLayer方法中为l设置更多的属性,但是首先,让我们运行程序并查看运行结果(自定义的层)。

虽然说只有几行代码,但是效果很酷,难道不是么!!!因为所有的view上面都有一个layer,所以你可以字任何view上面实现上面的效果。让我们再进一步去学习layer。

### CALayer的基本属性(Basic CALayer Properties) CALayer有很多属性,可以让你来改变它的展示形式,回想一下上面的代码对CALayer做了什么操作。 * 改变了CALayer的背景色为蓝色(默认是会没有背景色)。 * 给它设置了边框的宽度(默认是0到100)。 * 设置边框的颜色为红色(默认是黑色)。 * 最后阴影的不透明度为0.7(默认为0),这样它的阴影就会显示出来。然后改变了它的阴影半径为10(默认为3). 这些只是CALayer的一部分属性而已,让我们多尝试两次,添加下面的两行代码到setUpLayer()方法中
    l.contents = UIImage(named: "star")?.CGImage
    l.contentsGravity = kCAGravityCenter

CALayer的contents属性允许你把他的内容设置为一张图片,所以在此我们给其设置一个名字为star的图片。

你可以从 这里下载图片并且添加到你的工程中。 构建并运行项目,让我们来欣赏下这令人惊叹的艺术作品吧~

注意 你的star图片是剧中的,这是因为你设置layer的contentsGravity属性为kCAGravityCenter,这时你可能会想,我可不可以设置成其他的呢,OK,这是可行的,你可以设置contentsGravity属性为上,下,左,右,左上,左下,右上,右下~~~~

### 改变CALayer的外观(Changing the Layer’s Appearance) 为了更好玩,让我们添加一些手势识别器来操纵这个layer的外观,在Xcode,拖拽一个点击手势到viewForLayer上面,添加点击手势时如下图所示:

注意:如果你不熟悉手势识别器,你可以查看 Using UIGestureRecognizer with Swift.

重复上述操作添加一个缩放手势识别器到videForLayer,然后把storyboard中的每个手势和ViewController.swift建立联系,一个接着一个,并且把它们放在setUpLayer()的下面和当前类的结束标志大括号的前面。

在弹出的菜单当中,改变连接为Action,改变轻击手势的响应方法的名为tapGestureRecognized,缩放手势的响应方法名为pinchGestureRecognized,如下图所示:

改变方法 tapGestureRecognized(_:) 中的代码如下所示:

@IBAction func tapGestureRecognized(sender: UITapGestureRecognizer) {
  l.shadowOpacity = l.shadowOpacity == 0.7 ? 0.0 : 0.7
}
上面的代码可以在viewForLayer触发点击时间后改变l的shadowOpacity属性,从0.7和0之间相互切换。 view,你说什么?嗯,是的,你可以重写CALayer的hitTest(_:)方法来做同样的事情,事实上你会在本文的后面看到这样的实现。但是上述操作的逻辑是:hit testing是所有layer都可以响应的,但是它们却不能响应手势识别。这就是为什么我们把手势识别加在view的上面。 现在,改变pinchGestureRecognized(_:)中的代码如下所示:
@IBAction func pinchGestureRecognized(sender: UIPinchGestureRecognizer) {
  let offset: CGFloat = sender.scale < 1 ? 5.0 : -5.0
  let oldFrame = l.frame
  let oldOrigin = oldFrame.origin
  let newOrigin = CGPoint(x: oldOrigin.x + offset, y: oldOrigin.y + offset)
  let newSize = CGSize(width: oldFrame.width + (offset * -2.0), height: oldFrame.height + (offset * -2.0))
  let newFrame = CGRect(origin: newOrigin, size: newSize)
  if newFrame.width >= 100.0 && newFrame.width <= 300.0 {
    l.borderWidth -= offset
    l.cornerRadius += (offset / 2.0)
    l.frame = newFrame
  }
}
在这里我们创建了一个两个相反的offset,根据用户的缩放手势来改变layer的大小,边界的宽度和边界圆角的半径。 layer的圆角半径默认是0,也就是一个标准的90度的矩形,只要增加圆角的半径就会有圆角。想把一个正方形的layer变成圆形么,那就设置它的圆角半径为宽度的一半吧。 主语改变layer的圆角半径并不会裁剪它的contents(star图片),除非你设置它的masksToBounds属性为true。 构建并运行程序,并尝试对视图进行点击和缩放。

嗨,如果你肯再多润色一下,以一定会成为一个非常炫酷的头像制作者。

### 伟大的CALayer之旅(The Great CALayer Tour)

CALayer有很多属性和方法供我们使用,当然它的子类也有很多独特的属性和方法。

难道有什么方式来获取这些API的概述比来一场raywenderlich.com-style的旅游更好吗?

为了更好的学习本文的后半部分,你需要下面的东西:

  • The Layer Player App
  • The Layer Player Source Code

The Layer Player App是一个应用程序,包含10个不同类型个的CALayers的例子,这是你将在本文中学到的,先大概浏览一下吧:

下面的每一个例子,我建议你尝试一下我们上边给出的应用,并且有选择的查看我们所提供的源码。你并不需要在接下来的文章中跟着我们一句一句的敲代码。坐下来,像品尝一杯咖啡慢慢欣赏。

这些都是一些很好的例子,您可以参照例子添加不同类型的CALayer到你自己的项目中,我们希望你能喜欢。

### Example #1:CALayer 你已经看到一个CALayer的例子,设置了一些它的属性。 下面还有几个我上文没提到的关于CALayer的特性: * Layer可以有子Layer。就像View可以有子View,你可以利用Layer的这个特性实现一些很酷的效果。 * Layer的所有属性都具有动画。当你改变layer的一些属性的时候,它的动画是默认随着时间推移的,你也可以自己自定义这些动画遵守你的time rule。 * Layer是轻量级的。它比View更加轻量级,所以它能帮助你取得更好的性能。 * Layer有很多有用的属性,你已经见过几个,下面让我们见识一下Layer的更多属性。 如你所见,Layer具有很多有用的属性。让我们来参观CALayer属性的完整列表——一些很方便但是你却没有见过的。
// 1
let layer = CALayer()
layer.frame = someView.bounds

// 2
layer.contents = UIImage(named: "star")?.CGImage
layer.contentsGravity = kCAGravityCenter

// 3
layer.magnificationFilter = kCAFilterLinear
layer.geometryFlipped = false

// 4
layer.backgroundColor = UIColor(red: 11/255.0, green: 86/255.0, blue: 14/255.0, alpha: 1.0).CGColor
layer.opacity = 1.0
layer.hidden = false
layer.masksToBounds = false

// 5
layer.cornerRadius = 100.0
layer.borderWidth = 12.0
layer.borderColor = UIColor.whiteColor().CGColor

// 6
layer.shadowOpacity = 0.75
layer.shadowOffset = CGSize(width: 0, height: 3)
layer.shadowRadius = 3.0
someView.layer.addSublayer(layer)
上面的代码: 1 .创建一个CALayer实例并设置其大小为someView的大小。 2 .给layer的contents属性设置一个图片,并使其居中。注意layer的contents所使用的图片对象不是UIImage而是CGImage。 3 .使用这个过滤器当通过Image的contentsGravity的方法图像时,既可以用来改变大小(调整,调整方位和调整方位充满)和位置(中心,顶部,右上,右等等)。 前面的变化没有动画,如果geometryFlipped设置为true,几何位置和阴影就会上下颠倒。我们继续: 4 .设置背景颜色为Ray‘s最喜欢的绿色阴影,设置layer的不透明层并且可见。同时,设置layer不掩盖它的contents,这就意味着如果他的大小小于它的contents(star图片),则图片不会被裁减。 5 .设置Layer的圆角半径为Layer宽度的一半来创建一个圆边界的视觉效果。注意:这里所使用的layer的颜色是Quartz color的CGColor实例,而不是UIColor; 6 .创建阴影并设置shouldRasterize属性为true(下面即将讨论),然后添加layer到view上。 结果如下图所示:

CALayer有两个附加属性,可以提高性能:shouldRasterize和drawsAsynchronously。

shouldRasterize默认是false,当该值设置成true时可以提高layer的性能,因为一个层的内容只需要呈现一次。已动画的形式展现一些东西在屏幕上,但是并不需要再次改变其外观。

drawsAsynchronously和shouldRasterize是相反的,它的默认值也是false,当它设置为true来提高性能时会重新绘制层的内容,比如:当你使用CAEmitterLayer不断渲染的粒子动画。(后面会有一个CAEmitterLayer的例子)。

警告:考虑到之前给layer设置shouldRasterize或者drawsAsynchronously为 true,比较将这两者设置为true或者false时的性能你才能知道激活这些属性真正实际上提高了性能,当误用时,性能可能会一落千丈。
现在让我们把注意力集中到玩Layer上,它将包括操纵CALayer的很多属性:

控制着layer的各个属性的变化,这是一个非常有用的方式去感觉你能用CALayer能来什么~

注意:Layer并不是响应者连的一部分,正如例子CALayerPlayground中,layer并不会像View一样直接响应触摸或者手势。
然而,你可以 hit test它,正如你在示例项目中CATransformLayer所看到的,你也可以给layer加上自定义的动画,如示例项目中CAReplicatorLayer所示。
### Example #2: CAScrollLayer CAScrollLayer是只显示一部分可以滚动的layer。它是最基础的并且不能直接响应用户的触摸,也不能检测可滚动的范围,但可以检测可滚动layer的边界,所以它可以检测layer内容的边界以防止滚动超出其边界。 UIScrollView没有使用CAScrollLayer来实现这一功能,相反它直接改变自己的layer的边界。 你可以设置CAScrollLayer的滚动模式,水平和(或者)垂直,你也可以通过编程的方式告诉它滚动到一个特定的点或者矩形。
// In ScrollingView.swift
import UIKit

class ScrollingView: UIView {
  // 1
  override class func layerClass() -> AnyClass {
    return CAScrollLayer.self
  }
}

// In CAScrollLayerViewController.swift
import UIKit

class CAScrollLayerViewController: UIViewController {
  @IBOutlet weak var scrollingView: ScrollingView!

  // 2
  var scrollingViewLayer: CAScrollLayer {
    return scrollingView.layer as CAScrollLayer
  }

  override func viewDidLoad() {
    super.viewDidLoad()
    // 3
    scrollingViewLayer.scrollMode = kCAScrollBoth
  }

  @IBAction func tapRecognized(sender: UITapGestureRecognizer) {
    // 4
    var newPoint = CGPoint(x: 250, y: 250)
    UIView.animateWithDuration(0.3, delay: 0, options: .CurveEaseInOut, animations: {
      [unowned self] in
      self.scrollingViewLayer.scrollToPoint(newPoint)
      }, completion: nil)
  }

}
上面的代码所实现的: 1 .ScrollingView继承自UIView并重写了layerClass方法,使其返回一个CAScrollLayer的类型layer。这样做的选择性的创建一个新layer并添加为view的子layer,正如CALayer例子中所做的。 2 .用于简化获取ScrollingView的滚动layer。 3 .设置滚动模式为水平和垂直两个方向。 4 .当一个点击手势被接收到,创建一个新的CGPoint,然后让CAScrollLayer滚动到指定的点,由于scrollToPoint(_:) 和 scrollToRect(_:)没有默认的动画,所以此处加了 UIView 动画。 案例研究:ScrollingView的一个实例,装着一个图片,这个图片的尺寸比ScrollingView实例的尺寸还要大,当运行程序,点击View,结果如下图所示:

这里有一些经验法则来决定是否选择使用CAScrollLayer:

1 .如果你只是想要一些轻量级的,只需要以编程的方式滚动:考虑使用CAScrollLayer。

2 .如果你想让用户可以操作滚动,使用UIScrollView更好。想学习更多,请查看我们的18-part video tutorial series教程。

3 .如果你是滚动一个非常大的图片,考虑使用CATiledLayer。

### Example #3: CATextLayer CATextLayer可以更简单而且快速呈现纯文本或属性字符串。不像UILabel,CATextLayer没有UIFont的属性,只可以为其设置CTFontRef 或者CGFontRef 。 下面的代码,操纵了CATextLayer的字体,字体大小,颜色,对齐方式,wrapping and truncation,以及动画的变化:
// 1
let textLayer = CATextLayer()
textLayer.frame = someView.bounds

// 2
var string = ""
for _ in 1...20 {
  string += "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce auctor arcu quis velit congue dictum. "
}

textLayer.string = string

// 3
let fontName: CFStringRef = "Noteworthy-Light"
textLayer.font = CTFontCreateWithName(fontName, fontSize, nil)

// 4
textLayer.foregroundColor = UIColor.darkGrayColor().CGColor
textLayer.wrapped = true
textLayer.alignmentMode = kCAAlignmentLeft
textLayer.contentsScale = UIScreen.mainScreen().scale
someView.layer.addSublayer(textLayer)
针对上面的代码进行相应的解释: 1 .创建一个CATextLayer的实例,设置它的frame等于someView的bounds。 2 .创建字符串并将其赋给CATextLayer的实例。 3 .创建一个字体属性赋给CATextLayer的实例。 4 .设置文本左对齐(你可以选择设置默认,右边,中心或者自适应)。设置contentsScale为屏幕的scale。然后将layer作为subLayer添加到someView的layer上。 所有的Layer,不仅仅是CATextLayer,默认的比例因子是1,当合view绑定时,Layer的contentsScale需要设置为当前屏幕的比例因子。你需要手动设置你所创建的layer的contentsScale。否则它的contentsScale将是1,在retina屏幕上的显示效果不会太好。 如果你添加一个方形的someView,创建出的layer会看起来如下图所示:

文本的截断显示,当你想用省略号来截断文本,默认为none,但是你可以设置为开头,中间或者结尾。

Layer视图有很多控制器可以改变CATextLayer的属性:

### Example #4: AVPlayerLayer AVPlayerLayer是专门为AVFoundation而生的,它拥有一个AVPlayer可以播放视频文件(AVPlayerItems),下面会教大家怎么创建一个AVPlayerLayer:
override func viewDidLoad() {
  super.viewDidLoad()
  // 1
  let playerLayer = AVPlayerLayer()
  playerLayer.frame = someView.bounds

  // 2
  let url = NSBundle.mainBundle().URLForResource("someVideo", withExtension: "m4v")
  let player = AVPlayer(URL: url)

  // 3
  player.actionAtItemEnd = .None
  playerLayer.player = player
  someView.layer.addSublayer(playerLayer)

  // 4
  NSNotificationCenter.defaultCenter().addObserver(self, selector: "playerDidReachEndNotificationHandler:", name: "AVPlayerItemDidPlayToEndTimeNotification", object: player.currentItem)
}

deinit {
  NSNotificationCenter.defaultCenter().removeObserver(self)
}

// 5
@IBAction func playButtonTapped(sender: UIButton) {
  if playButton.titleLabel?.text == "Play" {
    player.play()
    playButton.setTitle("Pause", forState: .Normal)
  } else {
    player.pause()
    playButton.setTitle("Play", forState: .Normal)
  }

  updatePlayButtonTitle()
  updateRateSegmentedControl()
}

// 6
func playerDidReachEndNotificationHandler(notification: NSNotification) {
  let playerItem = notification.object as AVPlayerItem
  playerItem.seekToTime(kCMTimeZero)
}
上面代码的简单描述: 1 .创建一个新的player layer并且设置它的fame。 2 .用一个AV资源来创建一个player。 3 .设置player播放完成后什么也不做。(实际上是可以暂停或者切换到下一个资源)。 4 .添加AVPlayer监听通知,当他播放完一个资源。(需要移除observer在controller的deinit方法中)。 5 .当播放按钮被点击时,切换palyer的播放状态,设置按钮的标题。 请注意这只是一个简单的例子,在实际项目中,切换标题的文本通常被认为不是那么明智之举。 上面创建出来的AVPlayerLayer及其AVPlayer将呈现出AVPlayerItem实例的第一帧,如下图所示:

AVPlayerLayer有一些额外的属性:
* videoGravity设置视频的尺寸模式。
* readyForDisplay 用来检查视频是否加载完毕可以播放。

AVPlayer有不少额外的属性和方法。需要注意的是,回放速度从0到1。0表示暂停,1意味着视频播放速度处于正常(1 x)。

然而设置速度也以为这播放按照这个速度,换句话说调用avplayer的暂停方法和设置它的速度为0效果是一样的,正如调用avplayer的播放和设置其速度为1。

那么快进快退和循环播放呢?AVPlayerLayer同样有这些功能。设置速度比1大相当于让播放器按正常速度的多少倍进行播放。例如设置速度为2,则意味着以常速的2倍进行播放。

你可能会认为设置速度为负数则按正常速度的倍数反向播放。

不管之前以什么样的速率进行播放(快进)在回放开始之前,下面的AVPlayerItem的实例方法会被调用来验证是否可以在当前的速率下是否可以回放。

  • canPlayFastForward() for higher than 1
  • canPlaySlowForward() for between 0 and 1
  • canPlayReverse() for -1
  • canPlaySlowReverse() for between -1 and 0
  • canPlayFastReverse() for lower than -1

大多是的视频通常会以不同的速率播放着,但并不表明他们可以逆转。layer的player当然也包含着播放控制。

### Example #5: CAGradientLayer CAGradientLayer很容易混合两种或者两种以上的颜色在一起,它很适合做背景,为了配置它,你需要给它分配一个颜色的数组,设置起点和终点来指定CAGradientLayer的开始和结束的位置。 记住,起始点和结束点不是十分明确的点。相反,他们在单元坐标空间中定义,然后映射到层上,换句话说,x的值为1时表示点在layer边缘的右边,y的值为1时表示点在layer边缘的下边。 CAGradientLayer有一个类型的属性,尽管kCAGradientLayerAxial是唯一的选择,它会通过数组里面的颜色进行线性转换。 这意味着,你从起始点到终点画一条线(A),然后再画一条垂直于A的线(B),那么B上面各个点上的颜色是相同的。

或者你可以控制locations属性的数组的值(在0~1之间),这样的话它就会和颜色数组里面的值一一向对应,而两个值之间的间隔就是对应的颜色所占区域的百分比(渐变色)。

如果未设置locations属性,那么它默认是等间距的,如果设置了locations属性,则其元素个数和颜色数组的元素个数必须相等,否则会有不好的事情发生。

下面给出一个例子,怎么去创建一个CAGradientLayer:

let gradientLayer = CAGradientLayer()
gradientLayer.frame = someView.bounds
gradientLayer.colors = [cgColorForRed(209.0, green: 0.0, blue: 0.0),
  cgColorForRed(255.0, green: 102.0, blue: 34.0),
  cgColorForRed(255.0, green: 218.0, blue: 33.0),
  cgColorForRed(51.0, green: 221.0, blue: 0.0),
  cgColorForRed(17.0, green: 51.0, blue: 204.0),
  cgColorForRed(34.0, green: 0.0, blue: 102.0),
  cgColorForRed(51.0, green: 0.0, blue: 68.0)]
gradientLayer.startPoint = CGPoint(x: 0, y: 0)
gradientLayer.endPoint = CGPoint(x: 0, y: 1)
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
}
在上面的代码中,创建了一个CAGradientLayer,它的大小和someView的bounds相等,给它分配一个颜色数组,设置起始点和结束点,并且将它添加到它的父Layer上。结果如下图所示:

如此的多彩。你现在是否有编写出一个蝴蝶飞出App去亲吻你的鼻子的冲动。
实例代码中给你提供了一些控制器去改变起始点,终点,颜色数组和位置数组。

### Example #6: CAReplicatorLayer CAReplicatorLayer复制一个layer指定的次数,这能是你做出很酷的效果。 其复制出来的每一个layer都可以有自己的颜色和位置变化,其中的layer可以延迟绘图从而制造出一种动画效果在所有复制的layer中,layers的z轴的变化还会制造出一种3D的效果。下面给出一个例子:
// 1
let replicatorLayer = CAReplicatorLayer()
replicatorLayer.frame = someView.bounds

// 2
replicatorLayer.instanceCount = 30
replicatorLayer.instanceDelay = CFTimeInterval(1 / 30.0)
replicatorLayer.preservesDepth = false
replicatorLayer.instanceColor = UIColor.whiteColor().CGColor

// 3
replicatorLayer.instanceRedOffset = 0.0
replicatorLayer.instanceGreenOffset = -0.5
replicatorLayer.instanceBlueOffset = -0.5
replicatorLayer.instanceAlphaOffset = 0.0

// 4
let angle = Float(M_PI * 2.0) / 30
replicatorLayer.instanceTransform = CATransform3DMakeRotation(CGFloat(angle), 0.0, 0.0, 1.0)
someView.layer.addSublayer(replicatorLayer)

// 5
let instanceLayer = CALayer()
let layerWidth: CGFloat = 10.0
let midX = CGRectGetMidX(someView.bounds) - layerWidth / 2.0
instanceLayer.frame = CGRect(x: midX, y: 0.0, width: layerWidth, height: layerWidth * 3.0)
instanceLayer.backgroundColor = UIColor.whiteColor().CGColor
replicatorLayer.addSublayer(instanceLayer)

// 6
let fadeAnimation = CABasicAnimation(keyPath: "opacity")
fadeAnimation.fromValue = 1.0
fadeAnimation.toValue = 0.0
fadeAnimation.duration = 1
fadeAnimation.repeatCount = Float(Int.max)

// 7
instanceLayer.opacity = 0.0
instanceLayer.addAnimation(fadeAnimation, forKey: "FadeAnimation")
上面的代码做了什么: 1 .创建一个CAReplicatorLayer的实例,并设置它的frame为someView的bounds。 2 .设置CAReplicatorLayer的复制个数和它们之间的绘制的延迟(每隔1 / 30.0出现一个layer),设置其显示模式为2D,(preservesDepth = false)及其复制出来的layer的颜色为白色。 3 .设置其所显示的layer按次序颜色值递减。 4 .依次旋转每个创建及复制出来的layer,使其围成一个圆环。 5 .为CAReplicatorLayer的实例创建一个CALayer的实例并且设置它的frame,以确保CAReplicatorLayer所复制出来的第一个实例被置于somView的(someView.x/2, 0)位置处。这段代码设置了CALayer的实例的颜色,并把它添加到CAReplicatorLayer的实例上。 6 .为CAReplicatorLayer的所有实例设置透明度变化的动画。 7 .设置 CAReplicatorLayer的所有实例透明度为0,这样它们是透明的。直到每个实例被绘制出来,并且它们的颜色和透明度被设置。

我们所提供的demo LayerPlayer提供了一些控制器可以让你去改变这些属性:

### Example #7: CATiledLayer CATiledLayer以网格的形式异步绘制layer的content。这对大图片和其他的你只需要在某一时刻只看到其中的一小块的很有用,因为你可以不用一次性加载全部的content到内存中就可以看到其中的一部分内容。 有两种方法来处理绘制:以是继承UIView,用CATiledLayer反复绘制网格来填充view的背景,如下所示:
// In ViewController.swift
import UIKit

class ViewController: UIViewController {

  // 1
  @IBOutlet weak var tiledBackgroundView: TiledBackgroundView!

}

// In TiledBackgroundView.swift
import UIKit

class TiledBackgroundView: UIView {

  let sideLength = CGFloat(50.0)

  // 2
  override class func layerClass() -> AnyClass {
    return CATiledLayer.self
  }

  // 3
  required init(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
    srand48(Int(NSDate().timeIntervalSince1970))
    let layer = self.layer as CATiledLayer
    let scale = UIScreen.mainScreen().scale
    layer.contentsScale = scale
    layer.tileSize = CGSize(width: sideLength * scale, height: sideLength * scale)
  }

  // 4
  override func drawRect(rect: CGRect) {
    let context = UIGraphicsGetCurrentContext()
    var red = CGFloat(drand48())
    var green = CGFloat(drand48())
    var blue = CGFloat(drand48())
    CGContextSetRGBFillColor(context, red, green, blue, 1.0)
    CGContextFillRect(context, rect)
  }

}
上面的代码做了如下的事情: 1 .tiledBackgroundView设置它的frame为(150, 150, 300, 300)(在storyboard中设置)。 2 .重写layerClass方法,使tiledBackgroundView的layer为CATiledLayer而不是普通的CALayer。 3 .drawRect()方法中的srand48()函数会生成随机的颜色。设置小方格的尺寸,并使contentsScale等于屏幕的缩放比。 4 .重写drawRect()用CATiledLayer的实例(并且使用随机的颜色)来填充视图。 最终上面的代码生成一个6X6的网格就像一个正方形的瓷砖一样,如下图所示:

我们所提供的Demo LayerPlayer在CATiledLayer的上面绘制了一个五角星,

当你放大上面的图片是你会看到五角星变得模糊

这种模糊的程度是由layer的内容级别控制的。CATiledLayer有两个属性,levelsOfDetail levelsOfDetailBias。

原文解释的有点晦涩难懂,这是我摘抄自其它博客的:
原文如下:

levelsOfDetail, as its name aptly applies, is the number of levels of detail maintained by the layer. It defaults to 1, and each incremental level caches at half the resolution of the previous level. The maximum levelsOfDetail value for a layer is that which its bottom-most level of detail has at least a single pixel.
levelsOfDetailBias, on the other hand, is the number of magnified levels of detail cached by this layer. It defaults to 0, meaning no additional magnified levels will be cached, and each incremental level will be cached at double the preceding level’s resolution.
摘抄如下: levelsOfDetail 是指,从 UIScrollView 的1倍 zoomScale 开始,能够支持细节刷新的缩小级数。每一级是上一级的 1/2,所以假设levelsOfDetail=n,levelsOfDetailBias 不指定的话,CATiledLayer 将会在 UIScrollView 的 zoomScale 为以下数字时重新 drawLayer 2^-1 -> 2^-2 -> … -> 2^-n。也就是 1/2, 1/4, 1/8, 1/16, … , 1/2^n 在levelsOfDetailBias不指定的情况下,zoomScale大于0.5后就不会再drawLayer,所以若继续放大UIScrollView的话,画面将越来越模糊。这个时候levelsOfDetailBias就有用了。 levelsOfDetailBias = m表示,将原来的1/2,移到2^m倍的位置。假设levelsOfDetail = n,levelsOfDetailBias = m的话,会有如下队列: 2^m * 2^-1 -> 2^m * 2^-2 -> … -> 2^m * 2^-n 简化一下即 2^(m – 1) -> 2^(m – 2) -> 2^(m – 3) ->… -> 2^(m – n) 举例,levelsOfDetail = 3,levelsOfDetailBias = 3,则你的UIScrollView将会在以下zoomScale时drawLayer 2^(3 – 1) -> 2^(3 – 2) -> 2^(3 – 3) 。即 4 -> 2 -> 1。 特例是,levelsOfDetailBias > levelsOfDetail时,则每相差2倍就会drawLayer一下。可以简单理解成: levelsOfDetail表示一共有多少个drawLayer的位置 levelsOfDetailBias表示比1大的位置里有多少个drawLayer的位置(包括1) 以上若还没看懂的话,请看下图,图中灰色的格子表示跨界的格子,它们本来是同一个格子,被1这条线分割了。

回到正文:

例如:为上面模糊的CATiledLayer设置levelsOfDetailBias为5,将导致缓存水平放大2x,4x,8x,16x和32x,放大层看起来如下图所示:



很好玩,是么,等等,下面还有更多呢!

CATiledLayer的切片和小方块…抱歉,我想起来了youtobe上的忍者刀~~~~

但是,说真的,CATiledLayer还有一个我敢说比忍者刀更有用的目的,比如异步绘制一张很大的图片在ScrollView上。

你必须告诉它去绘制逻辑,当用户滑动时需要去绘制哪块,这里的性能很值得关注。

我们的Demo LayerPlayer包含一个UIImage的拓展,是一个名字为UIImage+TileCutter.swift的文件。我们的团队成员Nick Lockwood为它的终端应用适配了这段代码,他在它的优秀的作品iOS Core Animation: Advanced Techniques中有提到。

它的作用是切割源图为指定的大小,并且根据行数和列数为其命名。举个例子,windingRoad_6_2.png就是指第七列第三行的小图。



得到这些小图和它的坐标(行列),我们可以自定义一个UIView的子类去绘制这些小瓦片layer。

import UIKit

class TilingViewForImage: UIView {

  // 1
  let sideLength = CGFloat(640.0)
  let fileName = "windingRoad"
  let cachesPath = NSSearchPathForDirectoriesInDomains(.CachesDirectory, .UserDomainMask, true)[0] as String

  // 2
  override class func layerClass() -> AnyClass {
    return CATiledLayer.self
  }

  // 3
  required init(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
    let layer = self.layer as CATiledLayer
    layer.tileSize = CGSize(width: sideLength, height: sideLength)
  }

  // 4
  override func drawRect(rect: CGRect) {
    let firstColumn = Int(CGRectGetMinX(rect) / sideLength)
    let lastColumn = Int(CGRectGetMaxX(rect) / sideLength)
    let firstRow = Int(CGRectGetMinY(rect) / sideLength)
    let lastRow = Int(CGRectGetMaxY(rect) / sideLength)
    for row in firstRow...lastRow {
      for column in firstColumn...lastColumn {
        if let tile = imageForTileAtColumn(column, row: row) {
          let x = sideLength * CGFloat(column)
          let y = sideLength * CGFloat(row)
          let point = CGPoint(x: x, y: y)
          let size = CGSize(width: sideLength, height: sideLength)
          var tileRect = CGRect(origin: point, size: size)
          tileRect = CGRectIntersection(bounds, tileRect)
          tile.drawInRect(tileRect)
        }
      }
    }
  }

  func imageForTileAtColumn(column: Int, row: Int) -> UIImage? {
    let filePath = "\(cachesPath)/\(fileName)_\(column)_\(row)"
    return UIImage(contentsOfFile: filePath)
  }

}
上面的代码: 1 .创建view内容的宽高,根据图片的名字和Cache的路径来指定路径为UIImage的与拓展来保存小图片。 2 .重写layerClass() 返回CATiledLayer。 3 .重写init(_:),设置view所属layer(CATiledLayer)的titleSize大小。 4 .重写drawRect()方法根据他的行和列来绘制每个小图。 最后,设置设置其大小,让它可以在ScrollView上滚动。

瞧,你可以平滑的滚动大图(5120x3200),归功于CATiledLayer。

正如你上面所看到的动画,不过会有明显的快效应当你快速滑动时,减弱这种现象你可以把小图片的大小设置小点并且创建一个自定义的CATiledLayer的子类并重写fadeDuration()方法使其返回0:

class TiledLayer: CATiledLayer {
  override class func fadeDuration() -> CFTimeInterval {
    return 0.0
  }
}
### Example #8: CAShapeLayer CAShapeLayer使用可缩放的矢量进行绘制图形,使用它比使用图片要快得多,它的另一个优点是你不必再提供@2x和@3x两种尺寸的图片。 另外,你可以处理多种属性来定制画线的宽度,颜色,锐度,和两条线之间的链接方式,如果一条线形成一个封闭的区域,那么你可以指定该区域的颜色,线面给出一个例子:
import UIKit

class ViewController: UIViewController {

  @IBOutlet weak var someView: UIView!

  // 1
  let rwColor = UIColor(red: 11/255.0, green: 86/255.0, blue: 14/255.0, alpha: 1.0)
  let rwPath = UIBezierPath()
  let rwLayer = CAShapeLayer()

  // 2
  func setUpRWPath() {
    rwPath.moveToPoint(CGPointMake(0.22, 124.79))
    rwPath.addLineToPoint(CGPointMake(0.22, 249.57))
    rwPath.addLineToPoint(CGPointMake(124.89, 249.57))
    rwPath.addLineToPoint(CGPointMake(249.57, 249.57))
    rwPath.addLineToPoint(CGPointMake(249.57, 143.79))
    rwPath.addCurveToPoint(CGPointMake(249.37, 38.25), controlPoint1: CGPointMake(249.57, 85.64), controlPoint2: CGPointMake(249.47, 38.15))
    rwPath.addCurveToPoint(CGPointMake(206.47, 112.47), controlPoint1: CGPointMake(249.27, 38.35), controlPoint2: CGPointMake(229.94, 71.76))
    rwPath.addCurveToPoint(CGPointMake(163.46, 186.84), controlPoint1: CGPointMake(182.99, 153.19), controlPoint2: CGPointMake(163.61, 186.65))
    rwPath.addCurveToPoint(CGPointMake(146.17, 156.99), controlPoint1: CGPointMake(163.27, 187.03), controlPoint2: CGPointMake(155.48, 173.59))
    rwPath.addCurveToPoint(CGPointMake(128.79, 127.08), controlPoint1: CGPointMake(136.82, 140.43), controlPoint2: CGPointMake(129.03, 126.94))
    rwPath.addCurveToPoint(CGPointMake(109.31, 157.77), controlPoint1: CGPointMake(128.59, 127.18), controlPoint2: CGPointMake(119.83, 141.01))
    rwPath.addCurveToPoint(CGPointMake(89.83, 187.86), controlPoint1: CGPointMake(98.79, 174.52), controlPoint2: CGPointMake(90.02, 188.06))
    rwPath.addCurveToPoint(CGPointMake(56.52, 108.28), controlPoint1: CGPointMake(89.24, 187.23), controlPoint2: CGPointMake(56.56, 109.11))
    rwPath.addCurveToPoint(CGPointMake(64.02, 102.25), controlPoint1: CGPointMake(56.47, 107.75), controlPoint2: CGPointMake(59.24, 105.56))
    rwPath.addCurveToPoint(CGPointMake(101.42, 67.57), controlPoint1: CGPointMake(81.99, 89.78), controlPoint2: CGPointMake(93.92, 78.72))
    rwPath.addCurveToPoint(CGPointMake(108.38, 30.65), controlPoint1: CGPointMake(110.28, 54.47), controlPoint2: CGPointMake(113.01, 39.96))
    rwPath.addCurveToPoint(CGPointMake(10.35, 0.41), controlPoint1: CGPointMake(99.66, 13.17), controlPoint2: CGPointMake(64.11, 2.16))
    rwPath.addLineToPoint(CGPointMake(0.22, 0.07))
    rwPath.addLineToPoint(CGPointMake(0.22, 124.79))
    rwPath.closePath()
  }

   // 3
  func setUpRWLayer() {
    rwLayer.path = rwPath.CGPath
    rwLayer.fillColor = rwColor.CGColor
    rwLayer.fillRule = kCAFillRuleNonZero
    rwLayer.lineCap = kCALineCapButt
    rwLayer.lineDashPattern = nil
    rwLayer.lineDashPhase = 0.0
    rwLayer.lineJoin = kCALineJoinMiter
    rwLayer.lineWidth = 1.0
    rwLayer.miterLimit = 10.0
    rwLayer.strokeColor = rwColor.CGColor
  }

  override func viewDidLoad() {
    super.viewDidLoad()

    // 4
    setUpRWPath()
    setUpRWLayer()
    someView.layer.addSublayer(rwLayer)
  }

}
下面是上面代码所做的事情: 1 .创建颜色,路径和CAShapeLayer实例。 2 .绘制图层的路径,如果你不善于写这样的代码,你可以用PaintCode来绘制你想要的形状,它会自动帮你生成代码,或者你可以直接导入SVG或者PSD文件。 3 .设置CAShapeLayer的路径为步骤2中所绘制的路径,设置它的填充颜色为步骤1中所创建的颜色,填充规则设置为非零的默认值。 * 唯一的选项是奇偶。这个形状没有相交的路径,则此项设不设置没什么区别。 * 非零的规则计算规则为 左到右的路径为 +1, 右到左的路径为 -1。它会把所有值加起来,如果总数大于0,那么他将填补形状所形成的路径。 * 从本质上讲,非零的规则填充绘制形状上的所有点。 * 单双数的规则会计算形状上的所有路径,如果总数为技术,则填充形状。这绝对是一张图片胜过千个单词。 所有路径的单双数规则中,五角星形状的是偶数,所以五角星总体来书是不填满的,但是每个三角形的路径交叉后根据单双数规则计算出来的是奇数,所以五个小三角形需要填充颜色。
  • 调用绘制路径和设置layer的代码,并且添加layer到View的视图上。

这些代码绘制了raywenderlich.com的徽标:



如果你在PaintCode中绘制的话它会如下图所示:



Demo LayerPlayer中提供了很多控制器来改变CAShapeLayer的属性:



注意:你可能会注意到,我们跳过了LayerPlayer中的CAEAGLLayer,这是因为CAEAGLLayer被废弃了,取而代之的是CAMetalLayer,首次在iOS8中展现,你可以找到一个很强悍的教程来学习CAMetalLayer(https://www.raywenderlich.com/77488/ios-8-metal-tutorial-swift-getting-started)。
### Example #9: CATransformLayer CATransformLayer并不会像其他layer一样平铺的显示它的子Layer,它通常用来绘制3D图层。实际上它是它的subLayer的一个容器。它的所有子Layer都可以有自己的位置和透明度的变化,但是,它会忽视边框宽度和颜色等一些属性的变化。 你不能直接测试转换层,因为他没有一个2D坐标空间来映射一个触点,但是,可以测试单独的子层,这里有一个例子:
import UIKit

class ViewController: UIViewController {

  @IBOutlet weak var someView: UIView!

  // 1
  let sideLength = CGFloat(160.0)
  var redColor = UIColor.redColor()
  var orangeColor = UIColor.orangeColor()
  var yellowColor = UIColor.yellowColor()
  var greenColor = UIColor.greenColor()
  var blueColor = UIColor.blueColor()
  var purpleColor = UIColor.purpleColor()
  var transformLayer = CATransformLayer()

  // 2
  func setUpTransformLayer() {
    var layer = sideLayerWithColor(redColor)
    transformLayer.addSublayer(layer)

    layer = sideLayerWithColor(orangeColor)
    var transform = CATransform3DMakeTranslation(sideLength / 2.0, 0.0, sideLength / -2.0)
    transform = CATransform3DRotate(transform, degreesToRadians(90.0), 0.0, 1.0, 0.0)
    layer.transform = transform
    transformLayer.addSublayer(layer)

    layer = sideLayerWithColor(yellowColor)
    layer.transform = CATransform3DMakeTranslation(0.0, 0.0, -sideLength)
    transformLayer.addSublayer(layer)

    layer = sideLayerWithColor(greenColor)
    transform = CATransform3DMakeTranslation(sideLength / -2.0, 0.0, sideLength / -2.0)
    transform = CATransform3DRotate(transform, degreesToRadians(90.0), 0.0, 1.0, 0.0)
    layer.transform = transform
    transformLayer.addSublayer(layer)

    layer = sideLayerWithColor(blueColor)
    transform = CATransform3DMakeTranslation(0.0, sideLength / -2.0, sideLength / -2.0)
      transform = CATransform3DRotate(transform, degreesToRadians(90.0), 1.0, 0.0, 0.0)
    layer.transform = transform
    transformLayer.addSublayer(layer)

    layer = sideLayerWithColor(purpleColor)
    transform = CATransform3DMakeTranslation(0.0, sideLength / 2.0, sideLength / -2.0)
    transform = CATransform3DRotate(transform, degreesToRadians(90.0), 1.0, 0.0, 0.0)
    layer.transform = transform
    transformLayer.addSublayer(layer)

    transformLayer.anchorPointZ = sideLength / -2.0
    applyRotationForXOffset(16.0, yOffset: 16.0)
  }

  // 3
  func sideLayerWithColor(color: UIColor) -> CALayer {
    let layer = CALayer()
    layer.frame = CGRect(origin: CGPointZero, size: CGSize(width: sideLength, height: sideLength))
    layer.position = CGPoint(x: CGRectGetMidX(someView.bounds), y: CGRectGetMidY(someView.bounds))
    layer.backgroundColor = color.CGColor
    return layer
  }

  func degreesToRadians(degrees: Double) -> CGFloat {
    return CGFloat(degrees * M_PI / 180.0)
  }

  // 4
  func applyRotationForXOffset(xOffset: Double, yOffset: Double) {
    let totalOffset = sqrt(xOffset * xOffset + yOffset * yOffset)
    let totalRotation = CGFloat(totalOffset * M_PI / 180.0)
    let xRotationalFactor = CGFloat(totalOffset) / totalRotation
    let yRotationalFactor = CGFloat(totalOffset) / totalRotation
    let currentTransform = CATransform3DTranslate(transformLayer.sublayerTransform, 0.0, 0.0, 0.0)
    let rotationTransform = CATransform3DRotate(transformLayer.sublayerTransform, totalRotation,
      xRotationalFactor * currentTransform.m12 - yRotationalFactor * currentTransform.m11,
      xRotationalFactor * currentTransform.m22 - yRotationalFactor * currentTransform.m21,
      xRotationalFactor * currentTransform.m32 - yRotationalFactor * currentTransform.m31)
    transformLayer.sublayerTransform = rotationTransform
  }

  // 5
  override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
    if let location = touches.anyObject()?.locationInView(someView) {
      for layer in transformLayer.sublayers {
        if let hitLayer = layer.hitTest(location) {
          println("Transform layer tapped!")
          break
        }
      }
    }
  }

  override func viewDidLoad() {
    super.viewDidLoad()
   // 6
    setUpTransformLayer()
    someView.layer.addSublayer(transformLayer)
  }

}
上面的代码做了如下的事情: 1 .创建边长属性,立方体每一面的颜色,以及CATransformLayer的实例。 2 .创建layer并旋转它们最后添加到CATransformLayer的实例上,然后设置CATransformLayer的实例的Z轴秒点,旋转立方体,并把它加到视图上面。 3 .创建腐竹代码,来创建立方体每一面的颜色,转化角度为弧度(我认为弧度比角度更加直观)。 4 .基于x,y的偏移量使其旋转。注意:代码仅设置了sublayerTransform的旋转,但是这适用于CATransformLayer的子Layer。 5 .观察触摸和CATransformLayer的子Layer的周期。 6 .设置CATransformLayer,并将其添加到视图上。
这些CATransform3D属性代表一个矩阵的元素,包括一个矩形数组的行和列。
运行上述代码,设置someView的宽高都为250,运行结果如下图所示:

现在,试着点击立方体上的某一点,控制台上将会打印“Transform layer tapped!” 。

Demo LayerPlayer所给出的例子包括切换每个子层的不透明度,这使它很容易的应用到基于用户手势的3D变换。

### Example #10: CAEmitterLayer CAEmitterLayer呈现动画粒子(CAEmitterCell的实例)。CAEmitterLayer和CAEmitterCell都有属性去改变渲染速度,大小,形状,颜色,速度和寿命等等,下面给出一个例子:
import UIKit

class ViewController: UIViewController {

  // 1
  let emitterLayer = CAEmitterLayer()
  let emitterCell = CAEmitterCell()

  // 2
  func setUpEmitterLayer() {
    emitterLayer.frame = view.bounds
    emitterLayer.seed = UInt32(NSDate().timeIntervalSince1970)
    emitterLayer.renderMode = kCAEmitterLayerAdditive
    emitterLayer.drawsAsynchronously = true
    setEmitterPosition()
  }

  // 3
  func setUpEmitterCell() {
    emitterCell.contents = UIImage(named: "smallStar")?.CGImage

    emitterCell.velocity = 50.0
    emitterCell.velocityRange = 500.0

    emitterCell.color = UIColor.blackColor().CGColor
    emitterCell.redRange = 1.0
    emitterCell.greenRange = 1.0
    emitterCell.blueRange = 1.0
    emitterCell.alphaRange = 0.0
    emitterCell.redSpeed = 0.0
    emitterCell.greenSpeed = 0.0
    emitterCell.blueSpeed = 0.0
    emitterCell.alphaSpeed = -0.5

    let zeroDegreesInRadians = degreesToRadians(0.0)
    emitterCell.spin = degreesToRadians(130.0)
    emitterCell.spinRange = zeroDegreesInRadians
    emitterCell.emissionRange = degreesToRadians(360.0)

    emitterCell.lifetime = 1.0
    emitterCell.birthRate = 250.0
    emitterCell.xAcceleration = -800.0
    emitterCell.yAcceleration = 1000.0
  }

  // 4
  func setEmitterPosition() {
    emitterLayer.emitterPosition = CGPoint(x: CGRectGetMidX(view.bounds), y: CGRectGetMidY(view.bounds))
  }

  func degreesToRadians(degrees: Double) -> CGFloat {
    return CGFloat(degrees * M_PI / 180.0)
  }

  override func viewDidLoad() {
    super.viewDidLoad()

    // 5
    setUpEmitterLayer()
    setUpEmitterCell()
    emitterLayer.emitterCells = [emitterCell]
    view.layer.addSublayer(emitterLayer)
  }

  // 6
  override func traitCollectionDidChange(previousTraitCollection: UITraitCollection?) {
    setEmitterPosition()
  }

}
上面的代码: 1 .创建CAEmitterLayer和CAEmitterCell的实例。 2 .通过下面的步骤设置CAEmitterLayer * 设置layer的seed属性为一个随机数,接下来设置CAEmitterCell的属性,比如:velocity,下面会给出具体的解释。 * 设置CAEmitterCell的背景颜色和边框在指定的renderMode。 “` 注意:目前苹果的文档错误的指出,这个属性的值被定义在发射器模式,事实上renderMode的值是定义在发射器渲染顺序,默认的值是未定义的,其中包括: oldest first, oldest last, back to front and additive. “` * 设置drawsAsynchronously为true,这可能会提高性能,因为CAEmitterLayer需要不断重绘CAEmitterCell。 * 接下来,设置发射器的位置通过一个辅助方法。这是一个很好的案例研究如何设置drawsAsynchronously使其真正有一个积极的影响性能和平滑的动画。 3 .这一块进行了很多的操作。 * 它通过设置CAEmitterCell的contents为一张图片。 * 然后指定一个初始速度和最大方差(velocityRange);CAEmitterLayer使用上述方法创建一个随机数字生成器,范围内的随机值(初始值+ / -范围值)。这种随机化发生在范围结束内的任何属性。 * 颜色值设置为黑色,以允许方差来改变默认的白色,因为白色会导致粒子过于明亮。 * 设置一系列的颜色范围,使用相同的随机化velocityRange,这一次指定每种颜色的差异范围。速度的变化取决于CAEmitterCell每种颜色的生命周期。 * 接下来展示了如何实现一个圆锥形的CAEmitterCell,细节:它设置了CAEmitterCell的旋转速度和发射范围。此外,发射范围决定了CAEmitterCell分布在一个定义的锥形指定的弧度中。 * 设置CAEmitterCell的生命周期为1s,这个属性的默认值为0,所以如果你没有显式的设置这个值,那么你的CAEmitterCell就不会出现。所以,这个值必须设置为正数才能使CAEmitterCell显示出来。 * 最后,设置CAEmitterCell的xAcceleration,yAcceleration,这些值影响粒子发出的角度。 4 .辅助方法,将角度转化为弧度,设置CAEmitterCell的位置为视图的中心位置。 5 .设置CAEmitterLayer和CAEmitterCell,添加CAEmitterCell到CAEmitterLayer上,添加CAEmitterLayer到视图上。 6 .这是iOS8种新添加的方法,提供了一种方法去处理当前特征集合的改变,列入设备的旋转。不熟悉?是吧。去看看Section 1 of 8 by Tutorials,你会掌握它的~~ 万岁,我知道了很多的信息,这个过程是艰难的,但是你是聪明的~~~ 上面的代码的运行结构让我想起了youtobe上的The More You Know的广告。

Demo LayerPlayer包含的控制器可以调节上面的几个属性:





Where To Go From Here?

恭喜你,你已经完成的伟大的CALayer之旅,看到了10个例子如何使用CALayer和它的许多子类。

但不要停在这里,打开一个新项目或你正在参与的工作,看看如何利用层来实现更好的性能或者去发现新的东西。

像往常一样,如果你有任何问题或评论这篇文章或者使用CALayer时遇到的困惑,欢迎加入下面的讨论。

QQ:729376398(加QQ时请备注真实姓名)

你可能感兴趣的:(Swift,ios,CALayer,layer,swift)