CALayer及其各種子類

CoreAnimationXmind.png

這是 Core Animation 的系列文章伙狐,介紹了 Core Animation 的用法茧跋,以及如何進(jìn)行性能優(yōu)化伟姐。

  1. CoreAnimation基本介紹
  2. CGAffineTransform和CATransform3D
  3. CALayer及其各種子類
  4. CAAnimation:屬性動畫CABasicAnimation粥航、CAKeyframeAnimation以及過渡動畫刻诊、動畫組
  5. 圖層時間CAMediaTiming
  6. 計(jì)時器CADisplayLink
  7. 影響動畫性能的因素及如何使用 Instruments 檢測
  8. 圖像IO之圖片加載位岔、解碼如筛,緩存
  9. 圖層性能之離屏渲染、柵格化抒抬、回收池

我們已經(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 和第一個 sublayer 之間合成署辉,CAShapeLayer通過矢量圖形而非位圖繪制。使用CGPath指定顏色岩四、線寬和形狀哭尝,CAShapeLayer自動渲染圖層。你也可以使用 Core Graphics 直接向CALayercontents繪制路徑剖煌,但使用CAShapeLayer有以下這些優(yōu)點(diǎn):

  • 快速材鹦。CAShapeLayer使用硬件加速,比使用 Core Graphics 繪制速度快耕姊。

  • 節(jié)省內(nèi)存桶唐。CAShapeLayer無需像CALayer那樣創(chuàng)建 backing image。因此箩做,不會隨著 layer 變大莽红,占用更大內(nèi)存。

  • 超出 layer 邊框部分不會被裁剪邦邦。CAShapeLayer可以在bounds外繪制,不會像使用 Core Graphics 在CALayer繪制的圖形一樣被裁剪掉醉蚁。

  • 旋轉(zhuǎn)燃辖、縮放等變換操作后不會失真。由于CAShapeLayer是矢量圖(Vector graphics)网棍,可以通過數(shù)學(xué)公式計(jì)算獲得黔龟。放大時,不會像位圖(bitmap)那樣放大單個像素滥玷,也就不會出現(xiàn)線條或形狀鋸齒化的問題氏身。

1.1 創(chuàng)建 CGPath

CAShapeLayer可用于繪制任何可用CGPath表示的形狀。圖形不一定閉合惑畴,路徑不一定連續(xù)蛋欣,可以在一個CAShapeLayer中添加多個 shape。

設(shè)置一些屬性可以改變CAShapeLayer樣式如贷,如fillColor陷虎、strokeColor到踏、lineWidthlineCap(線末端樣式)尚猿、lineJoin(線之間接頭樣式)等窝稿,但一個CAShapeLayer只能有一個fillColorlineDashPattern凿掂、lineJoin等伴榔。如果需使用不同樣式、顏色庄萎,需創(chuàng)建多個 shape layer潮梯。

下面代碼顯示了使用CAShapeLayer繪制線筆畫,CAShapeLayerpath屬性是CGPathRef類型惨恭。這里使用UIBezierPath創(chuàng)建 path秉馏,省去了手動釋放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)

效果如下:

CAShapeLayer.png

1.2 圓角

使用CAShapeLayer可以創(chuàng)建圓角矩形脱羡。與cornerRadius相比萝究,CAShapeLayer允許指定單個角半徑。下面代碼創(chuàng)建三個圓角锉罐、一個直角的矩形:

        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)

CAShapeLayerpath屬性設(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)

效果如下:

CAMask.png

CALayermask屬性是CALayer類侨舆,使用方法與 sublayer 類似秒紧,相對于擁有它的圖層布局自身位置。與普通 sublayer 不同挨下,mask不是在父圖層內(nèi)繪制熔恢,其決定了父圖層的可見區(qū)域。

mask的顏色不重要臭笆,重要的是它的輪廓叙淌。與mask重合部分會被保留下來,mask以外部分會被隱藏愁铺。

CAMaskedImage.jpg

如果masklayer小于父圖層鹰霍,則只有與mask相交的父圖層部分可見,其他部分都會被隱藏茵乱。

2. CATransformLayer

在 3D 場景中茂洒,創(chuàng)建對象的層級結(jié)構(gòu)并將變換應(yīng)用于根視圖,整個層級結(jié)構(gòu)會隨之變換似将。

向容器中添加四個圖層获黔,不添加任何變換蚀苛。如下所示:

CAContainerLayer.png

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

CADistinctRotation.png

CALayer不能管理 3D 層級結(jié)構(gòu)中的深度玷氏,其只能將Z軸場景展平到單個層級堵未。為了解決這個問題,需使用CATransformLayer盏触。

與其他 layer 不同渗蟹,CATransformLayer不會將 sublayer 展平到 Z=0 的平面中,因此赞辩,它不支持CALayer的眾多功能:

  • 只渲染CATransformLayer的 sublayer雌芽。transform layer 的backgroundColorcontents辨嗽、邊緣樣式世落、描邊樣式等都不會生效。
  • 2D 圖像處理的屬性會被忽略糟需。包含filters屉佳、backgroundFilterscompositingFilter洲押、mask武花、masksToBounds和陰影樣式等。
  • opacity屬性會被單獨(dú)應(yīng)用到每個 sublayer杈帐,transform layer 不會形成合成組体箕。
  • Transform layer 沒有 2D 坐標(biāo)空間概念,不能將自身點(diǎn)映射到二維空間挑童。因此累铅,不要對 transform layer 應(yīng)用hitTest:方法。

下面代碼創(chuàng)建了四個 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é)果如下:

CATransformLayer.png

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

CATransformCALayer.png

3. CAGradientLayer

CAGradientLayer繪制背景色漸變的圖層。

Gradient layer 用于創(chuàng)建包含任意數(shù)量顏色的顏色漸變复旬。默認(rèn)情況下垦缅,顏色均勻分布在整個圖層上,但可以使用locations屬性指定顏色位置驹碍。

CAGradientLayer有以下屬性:

  • locations:元素為浮點(diǎn)類型的數(shù)組壁涎,值范圍為0至1凡恍,且只能遞增。如果為nil怔球,則均勻排布嚼酝。默認(rèn)為nil
  • colors:元素為CGColorRef類型的數(shù)組竟坛,默認(rèn)為nil闽巩。
  • startPoint:在圖層坐標(biāo)空間繪制時,漸變的起點(diǎn)担汤。使用單位坐標(biāo)系涎跨,并在繪制時映射到 layer 點(diǎn)坐標(biāo)。默認(rèn)值為(0.5, 0.5)崭歧。
  • endPoint:在圖層坐標(biāo)空間繪制時隅很,漸變的終點(diǎn)。使用單位坐標(biāo)系率碾,并在繪制時映射到 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)

效果如下:

CAGradientLayer.png

4. CAReplicatorLayer

CAReplicatorLayer用于創(chuàng)建 layer 的指定數(shù)量副本审编,副本間有不同的幾何坐標(biāo)、顯示屬性(delay歧匈、transform)和顏色等垒酬。常用屬性如下:

  • instanceCount:要創(chuàng)建的副本數(shù),包括原始 layer件炉。默認(rèn)值時1勘究,即不創(chuàng)建副本。
  • instanceDelay:指定副本顯示延時斟冕。默認(rèn)值為0.0秒口糕,即同步顯示。
  • instanceTransform:向前一個副本添加 transform磕蛇,得到當(dāng)前副本景描。默認(rèn)為CATransform3DIdentity
  • preservesDepth:是否將子圖層展平到平面中秀撇。默認(rèn)為false超棺。如果為true,則CAReplicatorLayer表現(xiàn)與CATransformLayer相似呵燕,同時受CATransformLayer同樣限制棠绘。
  • instanceColor:指定原始圖層的顏色。默認(rèn)為不透明白色。
  • instanceRedOffset:指定顏色紅色通道偏移量氧苍。向 k-1 實(shí)例添加偏移夜矗,得到 k 實(shí)例顏色。默認(rèn)為0.0让虐。

instanceGreenOffset紊撕、instanceBlueOffsetinstanceAlphaOffsetinstanceRedOffset類似澄干,只是通道不同逛揩。

下面的代碼在屏幕中央創(chuàng)建一個白色的 layer,使用CAReplicatorLayer創(chuàng)建由十個 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.png

CAReplicatorLayer可用于游戲中導(dǎo)彈發(fā)射后軌跡辩稽、粒子發(fā)射效果。此外从媚,還可以用于鏡像圖片逞泄。

設(shè)置負(fù)值的縮放因子可以獲得鏡像。這里將其封裝為單獨(dú)視圖拜效,后續(xù)使用時只需繼承自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
    }
}

效果如下:

CAReflection.png

開源項(xiàng)目ReflectionView實(shí)現(xiàn)了自適應(yīng)漸變淡出效果,淡出效果使用CAGradientLayer和 mask 實(shí)現(xiàn)紧憾。

5. CAScrollLayer

對于沒有進(jìn)行變換的 layer到千,bounds的大小與frame的大小一致。frame是由bounds赴穗、position派生而來憔四。因此,改變一個會影響另一個般眉。

如果想展示大圖層的一部分應(yīng)該如何做了赵?例如,有一個很大的圖片甸赃,或者一個長列表柿汛、文本,希望用戶可以隨意滑動埠对。在 iOS 中络断,可以使用UITableViewUIScrollView,Core Animation 中對應(yīng)的 layer 是什么呢项玛?

想要展示大圖一部分時妓羊,可以使用contentsRect屬性,但當(dāng)你的圖層有 sublayer 時稍计,每次滑動時都需要手動計(jì)算、更新所有 sublayer 位置裕循,這樣非常麻煩臣嚣。

這時可以使用CAScrollLayer净刮,CAScrollLayerscroll(to:)方法自動調(diào)整bounds的原點(diǎn),使圖層內(nèi)容看起來是在滑動硅则。由于 Core Animation 不能識別用戶手勢淹父,因此其不能將手勢轉(zhuǎn)換為滑動事件,另外也不會渲染滑動狀態(tài)條和滑動彈性效果怎虫。

下面使用CAScrollLayer創(chuàng)建一個類似UIScrollView的替代控件暑认。創(chuàng)建一個自定義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.png

CAScrollLayer類的以下方法實(shí)現(xiàn)了滾動功能:

  • scroll(to: CGPoint):將 layer 的原點(diǎn)設(shè)置為指定點(diǎn)。
  • scroll(to: CGRect):滾動內(nèi)容徒扶,確保指定矩形區(qū)域可見粮彤。

我們使用CAScrollLayer實(shí)現(xiàn)的 ScrollView 類沒有進(jìn)行任何邊界檢測,內(nèi)容可能會劃出可見區(qū)域并可繼續(xù)滾動姜骡。CAScrollLayer沒有UIScrollViewcontentSize概念导坟,因此沒有總可滑動區(qū)域概念。也就是劃動CAScrollLayer時圈澈,它只是調(diào)整bounds原點(diǎn)到指定位置惫周。

既然可以通過調(diào)整CALayerbounds獲得同樣效果,什么情況下需要使用CAScrollLayer康栈?事實(shí)上很少使用CAScrollLayer递递,UIScrollView沒有使用CAScrollLayer,而是直接操控 layer 的bounds進(jìn)行滾動谅将。

6. CATiledLayer

有時需要繪制的圖片特別大漾狼,而移動設(shè)備內(nèi)存非常有限,因此讀取整個圖片到內(nèi)存不是一種好的解決方案饥臂。

載入大圖會非常慢逊躁,常用的init(named:)contentsOfFile:方法會堵塞主線程,導(dǎo)致卡頓隅熙。圖片最大大小受設(shè)備內(nèi)存限制稽煤。屏幕上顯示的圖片最終都會被轉(zhuǎn)換為 OpenGL texture,而 OpenGL texture 有一個最大的大星羝荨(通常為2048*2048或4096*4096酵熙,因設(shè)備而異)。

如果要顯示的圖片大于單個 texture驰坊,即使圖片已經(jīng)存在于內(nèi)存中了匾二,Core Animation 也必須使用 CPU 而非 GPU 處理圖片,這時會明顯感受到內(nèi)存問題。

CATiledLayer通過把大圖分割為小圖解決上述性能問題察藐。當(dāng)需要渲染更多區(qū)域時皮璧,在一個或多個后臺線程調(diào)用draw(in:)方法,為繪制操作提供數(shù)據(jù)分飞。Drawing context 提供了 clip bounds 和 transform matrix悴务,用于確定請求圖塊的分辨率和 bounds。

使用setNeedsDisplay(_:)方法使圖層指定區(qū)域無效譬猫,但更新是異步的讯檐。且下一次的更新很可能不包含更新的內(nèi)容,但后續(xù)的更新會包含染服。

6.1 顯示多個小圖

下面展示一張大圖(2048*2048)别洪。為了獲得CATiledLayer的性能提升,需將大圖分割為多張小圖肌索。雖然可以使用代碼分割圖片蕉拢,但如果在運(yùn)行時加載圖片并分割,將會失去CATiledLayer提供的性能提升诚亚。這里直接使用分割好的小圖晕换,小圖大小為256*256,共64張站宗。

CATiledLayer添加到UIScrollView使用闸准,并實(shí)現(xiàn)draw(in:)方法。當(dāng)CATiledLayer需要加載新圖片時梢灭,會調(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()
    }
}

如下所示:

CATiledLayer.png

當(dāng)滑動圖片,會發(fā)現(xiàn)CATiledLayer載入小圖的時候會淡入到屏幕中敏释,這是CATiledLayer的默認(rèn)行為库快,可以使用fadeDuration屬性改變淡入時長或直接禁用掉。CATiledLayer不同于大部分UIKit和 Core Animation API钥顽,它支持多線程繪制义屏,draw(in:)方法可能在多線程并行調(diào)用,需確保該方法內(nèi)的繪制代碼線程安全蜂大。

不要嘗試直接修改CATiledLayercontents屬性闽铐,因?yàn)檫@樣會禁用它的異步機(jī)制,使其和普通的CALayer沒有區(qū)別奶浦。

7. CAEmitterLayer

CAEmitterLayer是一個高性能的粒子引擎兄墅,用來創(chuàng)建實(shí)時粒子動畫。例如澳叉,煙霧隙咸、火沐悦、雨等。

CAEmitterLayerCAEmitterCell實(shí)例的容器扎瓶,CAEmitterCell定義了粒子效果所踊。創(chuàng)建一個或多個CAEmitterCell對象作為不同類型粒子的模版,CAEmitterLayer基于模版產(chǎn)生粒子流概荷。

CAEmitterCell繼承自NSObject,和CALayer非常類似碌燕。CAEmitterCellcontents屬性可以定義為一個CGImage误证,還有很多屬性用于配制粒子的外觀和行為。這里不會詳細(xì)介紹每一個屬性修壕,你可以在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]

如下所示:

CAEmitterLayer.png

CAEmitterCell屬性可分為三類:

  • 屬性初始值,如color屬性指定一個可以混合contents圖片的顏色青团。在上述示例中譬巫,color被設(shè)置為橘色。
  • 屬性的變化范圍督笆。上述示例中芦昔,emissionRange被設(shè)置為360度,表示粒子可以向任意方向發(fā)射娃肿,粒子之間角度具有一定差值咕缎。可以通過設(shè)置一個小角度創(chuàng)建錐形效果料扰。
  • 屬性隨時間的變化凭豪。上述示例中,alphaSpeed值為-0.4,表示粒子的alpha每秒減少0.4祭芦,創(chuàng)建一種粒子遠(yuǎn)離過程中逐漸消失的效果成榜。

CAEmitterLayer屬性控制整個粒子系統(tǒng)的位置和形狀。CAEmitterLayer的有些屬性與CAEmitterCell屬性相同末早,設(shè)置CAEmitterLayer的屬性后,會與CAEmitterCell屬性相乘说庭。使用CAEmitterLayer屬性可以控制整個粒子系統(tǒng)效果然磷。還有以下兩個重要屬性:

  • preservesDepth:定義是否將粒子展平到平面中,默認(rèn)為false刊驴。如果為true姿搜,則該圖層將其粒子渲染為位于該圖層上層的三維坐標(biāo)空間寡润。啟用后,layer 的filters舅柜、backgroundFilters和陰影相關(guān)屬性效果是未定義的梭纹。
  • renderMode:控制粒子圖層在視覺上如何融合,默認(rèn)值為unordered致份。示例中使用additive变抽,即重疊部分亮度增加。

CAEmitterLayerscale氮块、seed绍载、spin等屬性乘數(shù),只影響新創(chuàng)建的粒子滔蝉,已經(jīng)發(fā)射出粒子不受影響击儡。例如,emitter 的scale值為1蝠引,發(fā)射一些粒子后修改scale為2阳谍。此時,已經(jīng)發(fā)射出去的粒子大小不受影響螃概,仍保持原來大小矫夯,新創(chuàng)建的粒子大小變?yōu)樵瓉矶丁?/p>

總結(jié)

這一部分介紹了多種圖層,以及使用這些圖層可以實(shí)現(xiàn)的效果谅年。像CATiledLayer茧痒、CAEmitterLayer等類都可以單獨(dú)寫成一篇文章,這里只作簡單介紹融蹂。另外旺订,CATextLayerCAMetaLayer超燃、AVPlayerLayer也是CALayer的子類区拳,這篇文章并未介紹,可以自行查閱文檔意乓。

CALayer并沒有針對所有情況都進(jìn)行性能優(yōu)化樱调。如果想要達(dá)到最佳性能,需根據(jù)需求選擇合適子類届良。下一篇文章CAAnimation:屬性動畫CABasicAnimation笆凌、CAKeyframeAnimation以及過渡動畫、動畫組將介紹顯式動畫士葫。

Demo名稱:CoreAnimation
源碼地址:https://github.com/pro648/BasicDemos-iOS/tree/master/CoreAnimation

上一篇:CGAffineTransform和CATransform3D

下一篇:CAAnimation:屬性動畫CABasicAnimation乞而、CAKeyframeAnimation以及過渡動畫、動畫組

參考資料:

  1. Introduction to 3D drawing in Core Animation (Part 1)

歡迎更多指正:https://github.com/pro648/tips

本文地址:https://github.com/pro648/tips/blob/master/sources/CALayer及其各種子類.md

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末慢显,一起剝皮案震驚了整個濱河市爪模,隨后出現(xiàn)的幾起案子欠啤,更是在濱河造成了極大的恐慌,老刑警劉巖屋灌,帶你破解...
    沈念sama閱讀 217,542評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件洁段,死亡現(xiàn)場離奇詭異,居然都是意外死亡共郭,警方通過查閱死者的電腦和手機(jī)祠丝,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來落塑,“玉大人纽疟,你說我怎么就攤上這事『读蓿” “怎么了?”我有些...
    開封第一講書人閱讀 163,912評論 0 354
  • 文/不壞的土叔 我叫張陵散吵,是天一觀的道長龙考。 經(jīng)常有香客問我,道長矾睦,這世上最難降的妖魔是什么晦款? 我笑而不...
    開封第一講書人閱讀 58,449評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮枚冗,結(jié)果婚禮上缓溅,老公的妹妹穿的比我還像新娘。我一直安慰自己赁温,他們只是感情好坛怪,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,500評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著股囊,像睡著了一般袜匿。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上稚疹,一...
    開封第一講書人閱讀 51,370評論 1 302
  • 那天居灯,我揣著相機(jī)與錄音,去河邊找鬼内狗。 笑死怪嫌,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的柳沙。 我是一名探鬼主播岩灭,決...
    沈念sama閱讀 40,193評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼偎行!你這毒婦竟也來了川背?” 一聲冷哼從身側(cè)響起贰拿,我...
    開封第一講書人閱讀 39,074評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎熄云,沒想到半個月后膨更,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,505評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡缴允,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,722評論 3 335
  • 正文 我和宋清朗相戀三年荚守,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片练般。...
    茶點(diǎn)故事閱讀 39,841評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡矗漾,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出薄料,到底是詐尸還是另有隱情敞贡,我是刑警寧澤,帶...
    沈念sama閱讀 35,569評論 5 345
  • 正文 年R本政府宣布摄职,位于F島的核電站誊役,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏谷市。R本人自食惡果不足惜蛔垢,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,168評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望迫悠。 院中可真熱鬧鹏漆,春花似錦、人聲如沸创泄。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽验烧。三九已至板驳,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間碍拆,已是汗流浹背若治。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留感混,地道東北人端幼。 一個月前我還...
    沈念sama閱讀 47,962評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像弧满,于是被迫代替她去往敵國和親婆跑。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,781評論 2 354

推薦閱讀更多精彩內(nèi)容