[关闭]
@Xiaojun-Jin 2015-04-08 07:01 字数 19521 阅读 2123

CALayer Learning Note

iOS CALayer


Note from author: This is a learning note of CALayer in iOS with Swift: 10 Examples written by Scott Gardner.

Contents Gravity

CALayer's coordinate (gravity) is opposite to UIView. For example:

  1. layer.backgroundColor = UIColor.blueColor().CGColor
  2. layer.borderWidth = 100.0
  3. layer.borderColor = UIColor.redColor().CGColor
  4. layer.shadowOpacity = 0.7
  5. layer.shadowRadius = 10.0
  6. layer.contents = UIImage(named: "star")?.CGImage
  7. layer.contentsGravity = kCAGravityTop

The star image will appear in the bottom section of the view. However, if we add layer.geometryFlipped = true to flip the geometry, then it'll be positioned at the top section as we expected.

  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(width : oldFrame.width + (offset * -2.0),
  7. height: oldFrame.height + (offset * -2.0))
  8. let newFrame = CGRect(origin: newOrigin, size: newSize)
  9. if newFrame.width >= 100.0 && newFrame.width <= 300.0 {
  10. l.borderWidth -= offset
  11. l.cornerRadius += (offset / 2.0)
  12. l.frame = newFrame
  13. }
  14. }

Here we're creating a positive or negative offset based on the user's pinch, and then adjusting the size of the layer's frame, width of its border and the border's corner radius.

Note that adjusting the corner radius doesn't clip the layer's contents (the star image) unless the layer's masksToBounds property is set to true.

CALayer Properties

  • Layer properties are animated.
  • Layers are lightweight.
  • Layers have tons of useful properties.

Use this filter when enlarging the image via contentsGravity:

  1. layer.magnificationFilter = kCAFilterLinear
  2. layer.geometryFlipped = false

which can be used to change both size (resize, resize aspect, and resize aspect fill) and position (center, top, top-right, right, etc.). These changes are not animated, and if geometryFlipped is not set to true, the positional geometry and shadow will be upside-down.

CALayer has two additional properties that can improve performance: shouldRasterize and drawsAsynchronously:

  • shouldRasterize is false by default, and when set to true it can improve performance because a layer's contents only need to be rendered once. It's perfect for objects that are animated around the screen but don't change in appearance.
  • drawsAsynchronously is sort of the opposite of shouldRasterize. It's also false by default. Set it to true to improve performance when a layer's contents must be repeatedly redrawn.

CAScrollLayer

What you can do with a CAScrollLayer is set its scrolling mode to horizontal and/or vertical, and you can programmatically tell it to scroll to a specific point or rect:

In ScrollingView.swift:

  1. import UIKit
  2. class ScrollingView: UIView
  3. {
  4. override class func layerClass() -> AnyClass
  5. {
  6. return CAScrollLayer.self
  7. }
  8. }

In CAScrollLayerViewController.swift:

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

Create a layer:
var l: CALayer { return sView.layer }
or
// creating a new layer and adding it as a sublayer
override class func layerClass() -> AnyClass { return CAScrollLayer.self }

P.S. for this demo, add a UIImageView as the subview of scrollingView, and setting its view mode with property Center

Check out what is [unowned self]: http://stackoverflow.com/questions/24320347/shall-we-always-use-unowned-self-inside-closure-in-swift

CATextLayer

With a block of code like this, it's possible to manipulate font, font size, color, alignment, wrapping and truncation, as well as animate changes

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

You need to set the contentsScale explicitly for layers you create manually, or else their scale factor will be 1 and you'll have pixilation on retina displays.

AVPlayerLayer

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

Setting rate also instructs playback to commence at that rate. In other words, calling pause() and setting rate to 0 does the same thing, as calling play() and setting rate to 1.

CAGradientLayer

To configure CAGradientLayer, you assign an array of CGColors, as well as a startPoint and an endPoint to specify where the gradient layer should begin and end.

  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)
  13. func cgColorForRed(red: CGFloat, green: CGFloat, blue: CGFloat) -> AnyObject {
  14. return UIColor(red: red/255.0, green: green/255.0, blue: blue/255.0, alpha: 1.0).CGColor as AnyObject
  15. }

CAReplicatorLayer

CAReplicatorLayer duplicates a layer a specified number of times, which can allow you to create some cool effects.

Each layer copy can have it's own color and positioning changes, and its drawing can be delayed to give an animation effect to the overall replicator layer. Depth can also be preserved to give the replicator layer a 3D effect.

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

replicatorLayer.instanceTransform is applied relative to the center of the replicator layer, i.e. the superlayer of each replicated sublayer.

CATiledLayer

There are a couple of ways to handle the drawing. One is to override UIView and use a CATiledLayer to repeatedly draw tiles to fill up view's background, like this:

In ViewController.swift:

  1. import UIKit
  2. class ViewController: UIViewController {
  3. // 1
  4. @IBOutlet weak var tiledBackgroundView: TiledBackgroundView!
  5. }

In TiledBackgroundView.swift:

  1. import UIKit
  2. class TiledBackgroundView: UIView {
  3. let sideLength = CGFloat(50.0)
  4. // 2
  5. override class func layerClass() -> AnyClass {
  6. return CATiledLayer.self
  7. }
  8. // 3
  9. required init(coder aDecoder: NSCoder) {
  10. super.init(coder: aDecoder)
  11. srand48(Int(NSDate().timeIntervalSince1970))
  12. let layer = self.layer as CATiledLayer
  13. let scale = UIScreen.mainScreen().scale
  14. layer.contentsScale = scale
  15. layer.tileSize = CGSize(width: sideLength * scale, height: sideLength * scale)
  16. }
  17. // 4
  18. override func drawRect(rect: CGRect) {
  19. let context = UIGraphicsGetCurrentContext()
  20. var red = CGFloat(drand48())
  21. var green = CGFloat(drand48())
  22. var blue = CGFloat(drand48())
  23. CGContextSetRGBFillColor(context, red, green, blue, 1.0)
  24. CGContextFillRect(context, rect)
  25. }
  26. }

CATiledLayer has two properties, levelsOfDetail and levelsOfDetailBias.

  • levelsOfDetail as its name aptly applies, is the number of levels of detail maintained by the layer.
  • levelsOfDetailBias on the other hand, is the number of magnified levels of detail cached by this layer.

For example, increasing the levelsOfDetailBias to 5 for the blurry tiled layer above would result in caching levels magnified at 2x, 4x, 8x, 16x and 32x.

Check out this link for more detail about levelsOfDetail and levelsOfDetailBias: http://www.cocoachina.com/bbs/read.php?tid-31201.html

CATiledLayer has another useful power: asynchronously drawing tiles of a very large image, for example, within a scroll view.

Layer Player includes a UIImage extension in a file named UIImage+TileCutter.swift. Fellow tutorial team member Nick Lockwood adapted this code for his Terminal app, which he provided in his excellent book, iOS Core Animation: Advanced Techniques.

Its job is to slice and dice the source image into square tiles of the specified size, named according to the column and row location of each tile; for example, windingRoad_6_2.png for the tile at column 7, row 3 (zero-indexed):

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

Then a view of that subclass type, sized to the original image's dimensions can be added to a scroll view.

Note that it is not necessary to match contentsScale to the screen scale, because you're working with the backing layer of the view directly vs. creating a new layer and adding it as a sublayer.

As you can see in the above animation, though, there is noticeable blockiness when fast-scrolling as individual tiles are drawn. Minimize this behavior by using smaller tiles (the tiles used in the above example were cut to 640 x 640) and by creating a custom CATiledLayer subclass and overriding fadeDuration() to return 0:

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

CAShapeLayer

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

Draws the shape layer's path. If writing this sort of boilerplate drawing code is not your cup of tea, check out PaintCode; it generates the code for you by letting you draw using intuitive visual controls or import existing vector (SVG) or Photoshop (PSD) files.

CATransformLayer

CATransformLayer does not flatten its sublayer hierarchy like other layer classes, so it's handy for drawing 3D structures.

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

To learn more about matrix transformations like those used in this example, check out 3DTransformFun project by fellow tutorial team member Rich Turton and Enter The Matrix project by Mark Pospesel.

Layer Player includes switches to toggle the opacity of each sublayer, and the TrackBall utility from Bill Dudney, ported to Swift, which makes it easy to apply 3D transforms based on user gestures.

CAEmitterLayer

CAEmitterLayer renders animated particles that are instances of CAEmitterCell. Both CAEmitterLayer and CAEmitterCell have properties to change rendering rate, size, shape, color, velocity, lifetime and more.

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

Awesome CALayer Demo: https://github.com/scotteg/LayerPlayer

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注