
這是 Core Animation 的系列文章,介紹了 Core Animation 的用法,以及如何進(jìn)行性能優(yōu)化。
我們已經(jīng)介紹了CALayer類、CGAffineTransform、CATransform3D,但 Core Animation 圖層不止用于設(shè)置圖片、背景色。這一篇文章介紹一些圖層類,進(jìn)一步擴(kuò)展 Core Animation 的能力。
1. CAShapeLayer
在第一篇文章CoreAnimation基本介紹中,介紹了使用CGPath創(chuàng)建任意形狀的陰影,無需使用圖片。如果可以創(chuàng)建任意形狀圖層就更好了。
CAShapeLayer在其坐標(biāo)空間中繪制三次貝塞爾曲線圖層,繼承自CALayer。
CAShapeLayer在 layer 的 contents 和第一個(gè) sublayer 之間合成,CAShapeLayer通過矢量圖形而非位圖繪制。使用CGPath指定顏色、線寬和形狀,CAShapeLayer自動(dòng)渲染圖層。你也可以使用 Core Graphics 直接向CALayer的contents繪制路徑,但使用CAShapeLayer有以下這些優(yōu)點(diǎn):
快速。
CAShapeLayer使用硬件加速,比使用 Core Graphics 繪制速度快。節(jié)省內(nèi)存。
CAShapeLayer無需像CALayer那樣創(chuàng)建 backing image。因此,不會(huì)隨著 layer 變大,占用更大內(nèi)存。超出 layer 邊框部分不會(huì)被裁剪。
CAShapeLayer可以在bounds外繪制,不會(huì)像使用 Core Graphics 在CALayer繪制的圖形一樣被裁剪掉。旋轉(zhuǎn)、縮放等變換操作后不會(huì)失真。由于
CAShapeLayer是矢量圖(Vector graphics),可以通過數(shù)學(xué)公式計(jì)算獲得。放大時(shí),不會(huì)像位圖(bitmap)那樣放大單個(gè)像素,也就不會(huì)出現(xiàn)線條或形狀鋸齒化的問題。
1.1 創(chuàng)建 CGPath
CAShapeLayer可用于繪制任何可用CGPath表示的形狀。圖形不一定閉合,路徑不一定連續(xù),可以在一個(gè)CAShapeLayer中添加多個(gè) shape。
設(shè)置一些屬性可以改變CAShapeLayer樣式,如fillColor、strokeColor、lineWidth、lineCap(線末端樣式)、lineJoin(線之間接頭樣式)等,但一個(gè)CAShapeLayer只能有一個(gè)fillColor、lineDashPattern、lineJoin等。如果需使用不同樣式、顏色,需創(chuàng)建多個(gè) shape layer。
下面代碼顯示了使用CAShapeLayer繪制線筆畫,CAShapeLayer的path屬性是CGPathRef類型。這里使用UIBezierPath創(chuàng)建 path,省去了手動(dòng)釋放CGPath的步驟。如下所示:
// Create path
let path = UIBezierPath()
path.move(to: CGPoint(x: 175, y: 100))
path.addArc(withCenter: CGPoint(x: 150, y: 100), radius: 25, startAngle: 0, endAngle: .pi * 2, clockwise: true)
path.move(to: CGPoint(x: 150, y: 125))
path.addLine(to: CGPoint(x: 150, y: 175))
path.addLine(to: CGPoint(x: 125, y: 225))
path.move(to: CGPoint(x: 150, y: 175))
path.addLine(to: CGPoint(x: 175, y: 225))
path.move(to: CGPoint(x: 100, y: 150))
path.addLine(to: CGPoint(x: 200, y: 150))
// Create shape layer
let shapelLayer = CAShapeLayer()
shapelLayer.strokeColor = UIColor.red.cgColor
shapelLayer.fillColor = UIColor.clear.cgColor
shapelLayer.lineWidth = 5
shapelLayer.lineJoin = .bevel
shapelLayer.lineCap = .round
shapelLayer.path = path.cgPath
// Add it to our view
view.layer.addSublayer(shapelLayer)
效果如下:

1.2 圓角
使用CAShapeLayer可以創(chuàng)建圓角矩形。與cornerRadius相比,CAShapeLayer允許指定單個(gè)角半徑。下面代碼創(chuàng)建三個(gè)圓角、一個(gè)直角的矩形:
let rect = CGRect(x: 0, y: 0, width: 100, height: 100)
let radii = CGSize(width: 20, height: 20)
let path = UIBezierPath.init(roundedRect: rect, byRoundingCorners: [.topRight, .bottomRight, .bottomLeft], cornerRadii: radii)
為CAShapeLayer的path屬性設(shè)置上述貝塞爾曲線,可以獲得圓角、直角組合的矩形。如果想要將 layer 的contents設(shè)置為同樣圖形,可以將CAShapeLayer賦值給mask屬性。如下所示:
// Create path
let rect = CGRect(x: 0, y: 0, width: 100, height: 100)
let radii = CGSize(width: 20, height: 20)
let path = UIBezierPath.init(roundedRect: rect, byRoundingCorners: [.topRight, .bottomRight, .bottomLeft], cornerRadii: radii)
let layer = CALayer()
layer.backgroundColor = UIColor.gray.cgColor
layer.position = view.center
layer.bounds = CGRect(x: 0, y: 0, width: 100, height: 100)
// Create mask layer
let maskLayer = CAShapeLayer()
maskLayer.path = path.cgPath
layer.mask = maskLayer
view.layer.addSublayer(layer)
效果如下:

CALayer的mask屬性是CALayer類,使用方法與 sublayer 類似,相對于擁有它的圖層布局自身位置。與普通 sublayer 不同,mask不是在父圖層內(nèi)繪制,其決定了父圖層的可見區(qū)域。
mask的顏色不重要,重要的是它的輪廓。與mask重合部分會(huì)被保留下來,mask以外部分會(huì)被隱藏。

如果masklayer小于父圖層,則只有與mask相交的父圖層部分可見,其他部分都會(huì)被隱藏。
2. CATransformLayer
在 3D 場景中,創(chuàng)建對象的層級結(jié)構(gòu)并將變換應(yīng)用于根視圖,整個(gè)層級結(jié)構(gòu)會(huì)隨之變換。
向容器中添加四個(gè)圖層,不添加任何變換。如下所示:

旋轉(zhuǎn)每個(gè) layer Y軸后,得到如下四個(gè)圖層:

CALayer不能管理 3D 層級結(jié)構(gòu)中的深度,其只能將Z軸場景展平到單個(gè)層級。為了解決這個(gè)問題,需使用CATransformLayer。
與其他 layer 不同,CATransformLayer不會(huì)將 sublayer 展平到 Z=0 的平面中,因此,它不支持CALayer的眾多功能:
- 只渲染
CATransformLayer的 sublayer。transform layer 的backgroundColor、contents、邊緣樣式、描邊樣式等都不會(huì)生效。 - 2D 圖像處理的屬性會(huì)被忽略。包含
filters、backgroundFilters、compositingFilter、mask、masksToBounds和陰影樣式等。 -
opacity屬性會(huì)被單獨(dú)應(yīng)用到每個(gè) sublayer,transform layer 不會(huì)形成合成組。 - Transform layer 沒有 2D 坐標(biāo)空間概念,不能將自身點(diǎn)映射到二維空間。因此,不要對 transform layer 應(yīng)用
hitTest:方法。
下面代碼創(chuàng)建了四個(gè) layer,其具有相同的x、y坐標(biāo),不同z坐標(biāo)。
private func testTransformLayerA() {
// Create the container as a CATransformLayer
let container = CATransformLayer()
// 如果使用CALayer,不能得到三維圖層。
// let container = CALayer()
container.frame = view.frame
view.layer.addSublayer(container)
// Planes data
let planesPosition = view.layer.position
let planeSize = CGSize(width: 100, height: 100)
// Create 4 planes
let purplePlane = addPlane(to: container, size: planeSize, position: planesPosition, color: UIColor.purple)
let redPlane = addPlane(to: container, size: planeSize, position: planesPosition, color: UIColor.red)
let orangePlane = addPlane(to: container, size: planeSize, position: planesPosition, color: UIColor.orange)
let yellowPlane = addPlane(to: container, size: planeSize, position: planesPosition, color: UIColor.yellow)
// Apply transform to the container
var t = CATransform3DIdentity
t.m34 = 1.0 / -500
t = CATransform3DRotate(t, .pi/3, 0, 1, 0)
container.transform = t
// Apply transform to the planes
t = CATransform3DIdentity
t = CATransform3DTranslate(t, 0, 0, 0)
purplePlane.transform = t
// Apply transform to the planes
t = CATransform3DIdentity
t = CATransform3DTranslate(t, 0, 0, -40)
redPlane.transform = t
// Apply transform to the planes
t = CATransform3DIdentity
t = CATransform3DTranslate(t, 0, 0, -80)
orangePlane.transform = t
// Apply transform to the planes
t = CATransform3DIdentity
t = CATransform3DTranslate(t, 0, 0, -120)
yellowPlane.transform = t
}
private func addPlane(to container: CALayer, size: CGSize, position: CGPoint, color: UIColor) -> CALayer {
let plane = CALayer()
plane.backgroundColor = color.cgColor
plane.opacity = 0.6
plane.frame = CGRect(x: 0, y: 0, width: size.width, height: size.height)
plane.position = position
plane.borderColor = UIColor.init(white: 1.0, alpha: 0.5).cgColor
plane.borderWidth = 3
plane.cornerRadius = 10
container.addSublayer(plane)
return plane
}
運(yùn)行結(jié)果如下:

如果使用CALayer替代CATransformLayer,效果如下:

3. CAGradientLayer
CAGradientLayer繪制背景色漸變的圖層。
Gradient layer 用于創(chuàng)建包含任意數(shù)量顏色的顏色漸變。默認(rèn)情況下,顏色均勻分布在整個(gè)圖層上,但可以使用locations屬性指定顏色位置。
CAGradientLayer有以下屬性:
-
locations:元素為浮點(diǎn)類型的數(shù)組,值范圍為0至1,且只能遞增。如果為nil,則均勻排布。默認(rèn)為nil。 -
colors:元素為CGColorRef類型的數(shù)組,默認(rèn)為nil。 -
startPoint:在圖層坐標(biāo)空間繪制時(shí),漸變的起點(diǎn)。使用單位坐標(biāo)系,并在繪制時(shí)映射到 layer 點(diǎn)坐標(biāo)。默認(rèn)值為(0.5, 0.5)。 -
endPoint:在圖層坐標(biāo)空間繪制時(shí),漸變的終點(diǎn)。使用單位坐標(biāo)系,并在繪制時(shí)映射到 layer 點(diǎn)坐標(biāo)。默認(rèn)值為(0.5, 1.0)。
下面代碼展示了如何創(chuàng)建包含三種顏色、指定漸變位置的圖層:
gradient.colors = [UIColor.red.cgColor, UIColor.yellow.cgColor, UIColor.green.cgColor]
gradient.locations = [0.0, 0.25, 0.5]
gradient.startPoint = CGPoint(x: 0, y: 0)
gradient.endPoint = CGPoint(x: 1, y: 1)
效果如下:

4. CAReplicatorLayer
CAReplicatorLayer用于創(chuàng)建 layer 的指定數(shù)量副本,副本間有不同的幾何坐標(biāo)、顯示屬性(delay、transform)和顏色等。常用屬性如下:
-
instanceCount:要?jiǎng)?chuàng)建的副本數(shù),包括原始 layer。默認(rèn)值時(shí)1,即不創(chuàng)建副本。 -
instanceDelay:指定副本顯示延時(shí)。默認(rèn)值為0.0秒,即同步顯示。 -
instanceTransform:向前一個(gè)副本添加 transform,得到當(dāng)前副本。默認(rèn)為CATransform3DIdentity。 -
preservesDepth:是否將子圖層展平到平面中。默認(rèn)為false。如果為true,則CAReplicatorLayer表現(xiàn)與CATransformLayer相似,同時(shí)受CATransformLayer同樣限制。 -
instanceColor:指定原始圖層的顏色。默認(rèn)為不透明白色。 -
instanceRedOffset:指定顏色紅色通道偏移量。向 k-1 實(shí)例添加偏移,得到 k 實(shí)例顏色。默認(rèn)為0.0。
instanceGreenOffset、instanceBlueOffset、instanceAlphaOffset與instanceRedOffset類似,只是通道不同。
下面的代碼在屏幕中央創(chuàng)建一個(gè)白色的 layer,使用CAReplicatorLayer創(chuàng)建由十個(gè) layer 構(gòu)成圓形的圖案。
var replicatorLayer = CAReplicatorLayer()
replicatorLayer.bounds = CGRect(x: 0, y: 0, width: view.bounds.size.width, height: view.bounds.size.height)
view.layer.addSublayer(replicatorLayer)
// Configure the replicator
replicatorLayer.instanceCount = 10
// Apply a transform for each instance
var transform = CATransform3DIdentity
transform = CATransform3DTranslate(transform, 0, 200, 0)
transform = CATransform3DRotate(transform, .pi / 5.0, 0, 0, 1)
transform = CATransform3DTranslate(transform, 0, -200, 0)
replicatorLayer.instanceTransform = transform
// Apply a color shift for each instance
replicatorLayer.instanceBlueOffset = -0.1
replicatorLayer.instanceGreenOffset = -0.1
// Create a sublayer and place it inside the replicator
let layer = CALayer()
layer.bounds = CGRect(x: 0, y: 0, width: 100, height: 100)
layer.position = view.layer.position
layer.backgroundColor = UIColor.white.cgColor
replicatorLayer.addSublayer(layer)
效果如下:

CAReplicatorLayer可用于游戲中導(dǎo)彈發(fā)射后軌跡、粒子發(fā)射效果。此外,還可以用于鏡像圖片。
設(shè)置負(fù)值的縮放因子可以獲得鏡像。這里將其封裝為單獨(dú)視圖,后續(xù)使用時(shí)只需繼承自ReflectionView即可。
class ReflectionView: UIView {
override class var layerClass: AnyClass {
return CAReplicatorLayer.self
}
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
setup()
}
private func setup() {
let layer = self.layer as! CAReplicatorLayer
layer.instanceCount = 2
// Move reflection instance below original and flip vertically
var transform = CATransform3DIdentity
let verticalOffset = self.bounds.size.height + 2
transform = CATransform3DTranslate(transform, 0, verticalOffset, 0)
transform = CATransform3DScale(transform, 1, -1, 0)
layer.instanceTransform = transform
// Reduce alpha of reflection layer
layer.instanceAlphaOffset = -0.6
}
}
效果如下:

開源項(xiàng)目ReflectionView實(shí)現(xiàn)了自適應(yīng)漸變淡出效果,淡出效果使用CAGradientLayer和 mask 實(shí)現(xiàn)。
5. CAScrollLayer
對于沒有進(jìn)行變換的 layer,bounds的大小與frame的大小一致。frame是由bounds、position派生而來。因此,改變一個(gè)會(huì)影響另一個(gè)。
如果想展示大圖層的一部分應(yīng)該如何做?例如,有一個(gè)很大的圖片,或者一個(gè)長列表、文本,希望用戶可以隨意滑動(dòng)。在 iOS 中,可以使用UITableView或UIScrollView,Core Animation 中對應(yīng)的 layer 是什么呢?
想要展示大圖一部分時(shí),可以使用contentsRect屬性,但當(dāng)你的圖層有 sublayer 時(shí),每次滑動(dòng)時(shí)都需要手動(dòng)計(jì)算、更新所有 sublayer 位置,這樣非常麻煩。
這時(shí)可以使用CAScrollLayer,CAScrollLayer的scroll(to:)方法自動(dòng)調(diào)整bounds的原點(diǎn),使圖層內(nèi)容看起來是在滑動(dòng)。由于 Core Animation 不能識別用戶手勢,因此其不能將手勢轉(zhuǎn)換為滑動(dòng)事件,另外也不會(huì)渲染滑動(dòng)狀態(tài)條和滑動(dòng)彈性效果。
下面使用CAScrollLayer創(chuàng)建一個(gè)類似UIScrollView的替代控件。創(chuàng)建一個(gè)自定義UIView,使用CAScrollLayer作為 backing layer,使用UIPanGestureRecognizer處理手勢。代碼如下:
class ScrollView: UIView {
override class var layerClass: AnyClass {
return CAScrollLayer.self
}
private func setup() {
// Enable clipping
layer.masksToBounds = true
backgroundColor = UIColor.lightGray
// Attach pan gesture recognizer
let recognizer = UIPanGestureRecognizer(target: self, action: #selector(self.pan(_:)))
addGestureRecognizer(recognizer)
}
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
setup()
}
@objc func pan(_ recognizer: UIPanGestureRecognizer) {
// Get the offset by subtracting the pan gesture
// Transform from the current bounds origin
var offset = self.bounds.origin
offset.x -= recognizer.translation(in: self).x
offset.y -= recognizer.translation(in: self).y
// Scroll the layer
layer.scroll(offset)
// Reset the pan gesture translation
recognizer.setTranslation(CGPoint.zero, in: self)
}
}
如下所示:

CAScrollLayer類的以下方法實(shí)現(xiàn)了滾動(dòng)功能:
-
scroll(to: CGPoint):將 layer 的原點(diǎn)設(shè)置為指定點(diǎn)。 -
scroll(to: CGRect):滾動(dòng)內(nèi)容,確保指定矩形區(qū)域可見。
我們使用CAScrollLayer實(shí)現(xiàn)的 ScrollView 類沒有進(jìn)行任何邊界檢測,內(nèi)容可能會(huì)劃出可見區(qū)域并可繼續(xù)滾動(dòng)。CAScrollLayer沒有UIScrollView中contentSize概念,因此沒有總可滑動(dòng)區(qū)域概念。也就是劃動(dòng)CAScrollLayer時(shí),它只是調(diào)整bounds原點(diǎn)到指定位置。
既然可以通過調(diào)整CALayer的bounds獲得同樣效果,什么情況下需要使用CAScrollLayer?事實(shí)上很少使用CAScrollLayer,UIScrollView沒有使用CAScrollLayer,而是直接操控 layer 的bounds進(jìn)行滾動(dòng)。
6. CATiledLayer
有時(shí)需要繪制的圖片特別大,而移動(dòng)設(shè)備內(nèi)存非常有限,因此讀取整個(gè)圖片到內(nèi)存不是一種好的解決方案。
載入大圖會(huì)非常慢,常用的init(named:)和contentsOfFile:方法會(huì)堵塞主線程,導(dǎo)致卡頓。圖片最大大小受設(shè)備內(nèi)存限制。屏幕上顯示的圖片最終都會(huì)被轉(zhuǎn)換為 OpenGL texture,而 OpenGL texture 有一個(gè)最大的大?。ㄍǔ?048*2048或4096*4096,因設(shè)備而異)。
如果要顯示的圖片大于單個(gè) texture,即使圖片已經(jīng)存在于內(nèi)存中了,Core Animation 也必須使用 CPU 而非 GPU 處理圖片,這時(shí)會(huì)明顯感受到內(nèi)存問題。
CATiledLayer通過把大圖分割為小圖解決上述性能問題。當(dāng)需要渲染更多區(qū)域時(shí),在一個(gè)或多個(gè)后臺線程調(diào)用draw(in:)方法,為繪制操作提供數(shù)據(jù)。Drawing context 提供了 clip bounds 和 transform matrix,用于確定請求圖塊的分辨率和 bounds。
使用setNeedsDisplay(_:)方法使圖層指定區(qū)域無效,但更新是異步的。且下一次的更新很可能不包含更新的內(nèi)容,但后續(xù)的更新會(huì)包含。
6.1 顯示多個(gè)小圖
下面展示一張大圖(2048*2048)。為了獲得CATiledLayer的性能提升,需將大圖分割為多張小圖。雖然可以使用代碼分割圖片,但如果在運(yùn)行時(shí)加載圖片并分割,將會(huì)失去CATiledLayer提供的性能提升。這里直接使用分割好的小圖,小圖大小為256*256,共64張。
把CATiledLayer添加到UIScrollView使用,并實(shí)現(xiàn)draw(in:)方法。當(dāng)CATiledLayer需要加載新圖片時(shí),會(huì)調(diào)用draw(in:)方法。
private func testTiledLayer() {
view.addSubview(scrollView)
// Add the tiled layer
let tileLayer = CATiledLayer()
tileLayer.frame = CGRect(x: 0, y: 0, width: 2048, height: 2048)
tileLayer.delegate = self
scrollView.layer.addSublayer(tileLayer)
// Configure the scroll view.
scrollView.contentSize = tileLayer.frame.size
// Draw layer
tileLayer.setNeedsDisplay()
}
extension LayersViewController: CALayerDelegate {
func draw(_ layer: CALayer, in ctx: CGContext) {
guard let layer = layer as? CATiledLayer else {
return
}
// Determine tile coordinate
let bounds = ctx.boundingBoxOfClipPath
let x: Int = Int(floor(bounds.origin.x / layer.tileSize.width))
let y: Int = Int(floor(bounds.origin.y / layer.tileSize.height))
// Load tile image
let imgName = "Snowman_0\(x)_0\(y)"
let imgPath = Bundle.main.path(forResource: imgName, ofType: "jpg")
guard let imgLocation = imgPath else { return }
let tileImage = UIImage(contentsOfFile: imgLocation)
// Draw tile
UIGraphicsPushContext(ctx)
tileImage?.draw(in: bounds)
UIGraphicsPopContext()
}
}
如下所示:

當(dāng)滑動(dòng)圖片,會(huì)發(fā)現(xiàn)CATiledLayer載入小圖的時(shí)候會(huì)淡入到屏幕中,這是CATiledLayer的默認(rèn)行為,可以使用fadeDuration屬性改變淡入時(shí)長或直接禁用掉。CATiledLayer不同于大部分UIKit和 Core Animation API,它支持多線程繪制,draw(in:)方法可能在多線程并行調(diào)用,需確保該方法內(nèi)的繪制代碼線程安全。
不要嘗試直接修改
CATiledLayer的contents屬性,因?yàn)檫@樣會(huì)禁用它的異步機(jī)制,使其和普通的CALayer沒有區(qū)別。
7. CAEmitterLayer
CAEmitterLayer是一個(gè)高性能的粒子引擎,用來創(chuàng)建實(shí)時(shí)粒子動(dòng)畫。例如,煙霧、火、雨等。
CAEmitterLayer是CAEmitterCell實(shí)例的容器,CAEmitterCell定義了粒子效果。創(chuàng)建一個(gè)或多個(gè)CAEmitterCell對象作為不同類型粒子的模版,CAEmitterLayer基于模版產(chǎn)生粒子流。
CAEmitterCell繼承自NSObject,和CALayer非常類似。CAEmitterCell的contents屬性可以定義為一個(gè)CGImage,還有很多屬性用于配制粒子的外觀和行為。這里不會(huì)詳細(xì)介紹每一個(gè)屬性,你可以在CAEmitterCell文檔中查看詳細(xì)介紹。
下面創(chuàng)建擁有不同速度、透明度的粒子,以視圖中心為emitterPosition向四周發(fā)射的爆炸效果。
// Create particle emitter layer
var replicatorLayer = CAReplicatorLayer()
emitter.position = view.layer.position
emitter.bounds = view.bounds
view.layer.addSublayer(emitter)
// Configure emitter
emitter.renderMode = .additive
emitter.emitterPosition = view.center
// Create a particle template
let cell = CAEmitterCell()
cell.contents = UIImage(named: "Spark")?.cgImage
cell.birthRate = 150
cell.lifetime = 5
cell.color = UIColor(red: 1.0, green: 0.5, blue: 0.1, alpha: 1.0).cgColor
cell.alphaSpeed = -0.4
cell.velocity = 50
cell.velocityRange = 50
cell.emissionRange = .pi * 2.0
// Add particle template to emitter
emitter.emitterCells = [cell]
如下所示:

CAEmitterCell屬性可分為三類:
- 屬性初始值,如
color屬性指定一個(gè)可以混合contents圖片的顏色。在上述示例中,color被設(shè)置為橘色。 - 屬性的變化范圍。上述示例中,
emissionRange被設(shè)置為360度,表示粒子可以向任意方向發(fā)射,粒子之間角度具有一定差值??梢酝ㄟ^設(shè)置一個(gè)小角度創(chuàng)建錐形效果。 - 屬性隨時(shí)間的變化。上述示例中,
alphaSpeed值為-0.4,表示粒子的alpha每秒減少0.4,創(chuàng)建一種粒子遠(yuǎn)離過程中逐漸消失的效果。
CAEmitterLayer屬性控制整個(gè)粒子系統(tǒng)的位置和形狀。CAEmitterLayer的有些屬性與CAEmitterCell屬性相同,設(shè)置CAEmitterLayer的屬性后,會(huì)與CAEmitterCell屬性相乘。使用CAEmitterLayer屬性可以控制整個(gè)粒子系統(tǒng)效果。還有以下兩個(gè)重要屬性:
- preservesDepth:定義是否將粒子展平到平面中,默認(rèn)為
false。如果為true,則該圖層將其粒子渲染為位于該圖層上層的三維坐標(biāo)空間。啟用后,layer 的filters、backgroundFilters和陰影相關(guān)屬性效果是未定義的。 - renderMode:控制粒子圖層在視覺上如何融合,默認(rèn)值為
unordered。示例中使用additive,即重疊部分亮度增加。
像
CAEmitterLayer的scale、seed、spin等屬性乘數(shù),只影響新創(chuàng)建的粒子,已經(jīng)發(fā)射出粒子不受影響。例如,emitter 的scale值為1,發(fā)射一些粒子后修改scale為2。此時(shí),已經(jīng)發(fā)射出去的粒子大小不受影響,仍保持原來大小,新創(chuàng)建的粒子大小變?yōu)樵瓉矶丁?/p>
總結(jié)
這一部分介紹了多種圖層,以及使用這些圖層可以實(shí)現(xiàn)的效果。像CATiledLayer、CAEmitterLayer等類都可以單獨(dú)寫成一篇文章,這里只作簡單介紹。另外,CATextLayer、CAMetaLayer、AVPlayerLayer也是CALayer的子類,這篇文章并未介紹,可以自行查閱文檔。
CALayer并沒有針對所有情況都進(jìn)行性能優(yōu)化。如果想要達(dá)到最佳性能,需根據(jù)需求選擇合適子類。下一篇文章CAAnimation:屬性動(dòng)畫CABasicAnimation、CAKeyframeAnimation以及過渡動(dòng)畫、動(dòng)畫組將介紹顯式動(dòng)畫。
Demo名稱:CoreAnimation
源碼地址:https://github.com/pro648/BasicDemos-iOS/tree/master/CoreAnimation
上一篇:CGAffineTransform和CATransform3D
下一篇:CAAnimation:屬性動(dòng)畫CABasicAnimation、CAKeyframeAnimation以及過渡動(dòng)畫、動(dòng)畫組
參考資料:
歡迎更多指正:https://github.com/pro648/tips
本文地址:https://github.com/pro648/tips/blob/master/sources/CALayer及其各種子類.md