CALayer教程:入门指南+详细讲解(CALayer Tutorial Getting Started)

阎晋
2023-12-01

如你所知,你在iOS APP 中看到的一切都是一个视图,比如button viewstable viewsslider views,还有包含其他视图的父视图等。 但你可能不知道的是,在iOS中每一个view都是由一个叫layer的类支撑的——一个具体的CALayer层。 在本文中,你将了解什么是CALayer,它是如何工作的。你还可以看到使用CALayers做出的很酷效果的十个例子,如 shapes(形状),gradients(渐变),和particle system(粒子系统)。 本文假定你已经熟悉iOS应用开发和Swift的基础知识,包括用storyboard构建你的UI

Tips:如果你没有相关的知识也不要着急,很高兴的告诉你这儿有不少相关的教程和书籍,比如 Learn to Code iOS Apps with SwiftThe iOS Apprentice

开始(Getting Started)


了解层级最简单的方法就是在实战中看他们是如何工作的,那么就让我们从头开始创建一个简单的项目来玩转层级。 做好写代码的准备了吗?OK,打开Xcode然后按以下步骤:

  1. menu中选择File\\New\\Project…
  2. 在对话框中选择iOS\\Application\\Single View Application
  3. 点击Next,输入CALayerPlayground作为你的工程名,然后填写你的organization nameidentifier
  4. 语言选择Swift,然后设备选择Universal
  5. 不选择Core Data,然后点击Next
  6. 选择一个合适的地方存放你的项目(这里我将我的工程放在user目录下一个叫Source的文件夹里),然后点击Creat, 现在,你已经有了一个工程,那么接下来的一步就是创建一个view
  7. 在工程目录导航中,选择Main.storyboard
  8. menu中选择View\\Assistant Editor\\Show Assistant Editor,然后选择View\\Utilities\\Show Object Library如果还没有显示出来的话
  9. 同时,选择Editor\\Canvas\\Show Bounds Rectangles,然后你就可以看到你将要添加视图场景的边界轮廓了
  10. 从你的Object library中拖一个view放在你的视图控制器中,选中它,然后在Size inspectorView\\Utilities\\Show Size Inspector)中设置xy150widthheight300
  11. 保持这个view的选中状态,在auto layout toolbarbottom-right of the storyboard)中点击Align按钮,然后选中Horizontal Center in ContainerVertical Center in Container,将它们的值设为0,点击添加这两个约束
  12. 点击Pin这个按钮,然后选择WidthHeight,确保这两个值设置为300,然后增加这两个约束

最后,将这个view拖拽到你刚刚创建的ViewController.swiftviewDidLoad()这个方法的右上方,在弹出的窗口中,设置名称为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 中每一个view都有与之相关联的layer,你可以用yourView.layer这个方法重新得到layer。上面这段代码所做的第一件事情就是创建一个名为“l”“L”的小写)的属性,用来访问viewForLayerlayer。 这段代码同时还调用了setUpLayer这个方法来设置了layer的一些属性,比如一个阴影、一个蓝色的背景和一个巨大的红色边框。关于setUpLayer()你将会了解更多,但是首先,先让我们在模拟器上(我选择的是iPhone 6)编译并运行我们的这个工程,来看看我们的自定义layer,效果如下:

就简单的几行代码做出了很炫的效果,不是吗?再次说一下,因为每一个 view都是由一个 layer支撑的,所以你可以在你的 APP中对任意一个 view做许多事情,具体如下

CALayer的一些基本属性(Basic CALayer Properties)


CALayer有一些属性可以让你自定义它的外观。回想一下我们已经做的事情:

  • 把它的背景色从默认的无色变成了蓝色
  • 通过改变边框的宽度从默认的0100给它增加了一个边框
  • 改变了它边框的颜色,从默认的黑色变成红色
  • 最后,通过改变它阴影的透明度从默认的0(透明)到0.7给了它一个阴影,这样阴影就显示出来了,然后你又使它的阴影的半径从默认的3增加到了10

这些只是CALayer的几个属性。让我们在尝试两次,在setUpLayer()这个方法下添加如下两行:

 l.contents = UIImage(named: "star")?.CGImage
 l.contentsGravity = kCAGravityCenter
复制代码

CALayercontents属性可以将图层的内容设置成一个image,在这里我们将它设置成了一个名为“star的一个图片。为了做到这些,你需要将这个图片添加到你的工程中,图片可以在 这里 下载。 编译并运行,然后花一点时间欣赏这个美丽的艺术品吧,如下图

需要注意的是在这里这个 “star”是在中心位置的,这是因为你设置了 contentsGravity这个属性为 kCAGravityCenter,如果你愿意,你还可以将这个 “star”的重心设置在顶部,右上,右,下,右,下,下左,左,左上角。(为了更好地理解,我将 contentsGravity这个属性列表在这里展示出来,如下)

/** Layer `contentsGravity' values. **/
@available(iOS 2.0, *)
public let kCAGravityCenter: String

@available(iOS 2.0, *)
public let kCAGravityTop: String

@available(iOS 2.0, *)
public let kCAGravityBottom: String

@available(iOS 2.0, *)
public let kCAGravityLeft: String

@available(iOS 2.0, *)
public let kCAGravityRight: String

@available(iOS 2.0, *)
public let kCAGravityTopLeft: String

@available(iOS 2.0, *)
public let kCAGravityTopRight: String

@available(iOS 2.0, *)
public let kCAGravityBottomLeft: String

@available(iOS 2.0, *)
public let kCAGravityBottomRight: String
复制代码

Tips:关于CALayercontentscontentsGravity等属性这里有一套关于Core Animation的详细介绍,有兴趣的可以看下 iOS-Core-Animation-Advanced-Techniques(一)(共有七篇,这里只列出其中的一篇)

改变Layer的外观(Changing the Layer’s Appearance)


为了好玩一点,让我们在这个layer上添加一些手势来操纵它的外观,在Xcode中,拖拽一个单击手势(tap gesture recognizer)在viewForLayer这个对象上。作为参考,添加手势应该像下面这样,

Tips:如果你对手势这方面的知识不熟悉,可以看看 Using UIGestureRecognizer with Swift

用同样的方法再在viewForLayer上添加一个捏合手势(pinch gesture recognizer)。 然后按住control键将每个手势从storyboard连接到ViewController.swift上。然后依次把他们放在setUpLayer()和本类的右大括号之间。 在弹出视图中,将connection切换到Action这个选项,然后将单击手势(tap gesture recognizer)命名为tapGestureRecognized,将捏合手势(pinch gesture recognizer)命名为pinchGestureRecognized。如例:

tapGestureRecognized(_:)这个方法改成下面这样:

@IBAction func tapGestureRecognized(sender: UITapGestureRecognizer) { 
l.shadowOpacity = l.shadowOpacity == 0.7 ? 0.0 : 0.7
}
复制代码

上面这段代码的意思就是当点击viewForLayer的时候,它的layer的阴影的透明度(shadow opacity)在0.7-0之间变化。 当然,如你所说,你也可以通过重写CALayerhitTest(_:)方法来做相同的事情。事实上你也可以在本文的最后看到这种方法。 现在改变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
    }
}
复制代码

在这里基于用户的捏合手势可以创建一个正的或者负的偏移量来改变layer的大小(frame)、边框的宽度(width)和边框的圆角半径(corner radius)。 每一个layer默认的圆角半径(corner radius)是0,这就意味着如果layer有一个90°的角,那么他就是一个标准的矩形。圆角将会随着半径的增大而产生。那么如何将一个方形的layer变成一个圆形的呢?很简单,就是把它的圆角半径(corner radius)设为宽度的一半。 需要注意的是调整圆角半径并不会裁剪layercontents“star”这个图),除非你将layermasksToBounds属性设置为true。 编译并且运行,试着点击和捏合这个view

嘿,多一点改善,你将会拥有一个非常漂亮的头像!

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


当然, CALayer还有更多的属性和方法用来操作,比如它的有些子类,拥有一些特别的属性和方法。 除了本文以外,你将会需要下面的这些:

这是一个很方便的app包含了本文中的所有10个例子,下面是这十个例子的预览:

当我们在阅读下面每个例子的时候,我建议大家都运行一下这个app,然后有选择的看看所提供的源代码。其实你并不需要对本文的余下部分编写代码,所有的代码都已经提供,所以,坐下来,轻松地阅读吧! 这些对你来说应该是一些很好的例子当你需要添加一些 CALayers的一些类到自己公程中的时候,我们希望你能喜欢!

例一: CALayer


在上面我们已经看到过一个使用CALayer的例子,并且设置了一些属性。然而还有一些关于CALayer的属性,在上面并没有提到:

  • **Layers can have sublayers. ** 就像view可以拥有子view一样,layer可以拥有子layer,利用这个特点你可以做出一些特殊的效果。
  • **Layer properties are animated.**当你改变一个layer的属性的时候,它默认的属性就会随着时间变化。当然你也可以根据时间自定义这些动画。
  • Layers are lightweight. Layers是比Views更加轻量的,因此使用Layer会帮助你获得更好地性能。
  • Layers have tons of useful properties.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.将layercontents属性设置一个image,并且把这个image放在这个layer的中心。需要注意的是这里需要使用的是CGImage
  • 3.通过contentsGravity来放大图像的时候选择用kCAFilterLinear,这个属性可以调整大小(形状、尺寸)和位置(上、下、左、右)。在之前的变化中并没有做动画,但是如果geometryFlipped没有设置为true,它阴影的几何位置将会颠倒,继续:
  • 4.我们设置了它的背景色(r/g/b)和透明度(透明度为1),与此同时我们设置了masksToBoundsfalse,这意味着如果它的大小小于其contents(例子中的star图片),图像就不会裁剪。
  • 5.我们设置了layer的圆角半径为宽度的一半,并设置了一个宽度为12的圆形边界效果,需要注意的是这里设置颜色需要用CGColor
  • 6.创建一个阴影并且设置shouldRasterizetrue(下面会讲到),然后添加到view的层级中

结果如下:

CALayer还有有两个可以提高性能的附加属性: shouldRasterizedrawsAsynchronously

  • shouldRasterize这个属性默认的是false,但是当你把它设置成true的时候就会提升性能,因为这个设置使图层的内容只呈现一次。它非常适合那些在屏幕上做动画,但是不需要改变外形的对象。
  • drawsAsynchronously这个属性几乎是与shouldRasterize相反的。它的默认值也是false,当一个Layer的内容需要反复重画的时候把它设置成true将会提升性能,比如说使用粒子发射器(具体请见后面CAEmitterLayer这个例子)。

注意:考虑到给一个指定的LayershouldRasterizedrawsAsynchronously这两个属性设置为true,排除其他影响,比较truefalse之后你会发现设置为true之后性能的确提升了,但是注意请不要误用,否则性能会急速下降,效果适得其反!

现在,让我们简短的把注意力集中在这个Layer Player上,它能控制操纵layer的许多属性:

试玩一下里面的各种控制——这将会是你对CALayer的功能理解的最有效地方式!

注意:图层并不是响应链的一部分,所以它不会像view那样直接响应触摸或者手势,就像你在CALayerPlayground这个例子中看到的一样。 但是,你可以对它们进行hit test,同样你可以在CATransformLayer里面看见相关的代码。当然你也可以对Layer添加一些自定义的动画,在后面的CAReplicatorLayer里面我们同样会看到!

例二: CAScrollLayer


CAScrollLayer显示的是滚动层的一部分,它使非常基础的,不能直接的响应用户的触摸,所以它可以在它的滚动范围内做很多炫酷的事情! UIScrollView并不能直接使用CAScrollLayer来做这些事情,它是直接通过改变layerbounds来做的。对于CAScrollLayer,你需要做的就是设置它的滚动方向(水平或者竖直),同时你可以动态的设置他滚动到一个point或者rect

// 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、重写了layerClass()这个方法,返回一个CAScrollLayer,这是快速创建一个新的layer的一种方式,就像在CALayer这个例子中一样。
  • 2、定义了scrollingViewLayer
  • 3、设置了可以在水平和竖直两个方向上滑动
  • 4、当点击手势被识别的时候,就会创建一个新的点,scrolling layer就会以动画的形式滑动到这一点。需要注意的是:scrollToPoint(_:)scrollToRect(_:)这两个方法并不会自动做动画

例子分析:一个ScrollingView包含了一个比他自身还要大的imageView,当你运行所有的代码并且点击上面的view时,效果如下图:

本例子中,包含控制水平和竖直方向滑动的控件 下面有一些当你使用(或者不使用)CAScrollLayer的一些经验:

  • 如果你需要的是轻量的,并且只能用编程的方式滑动,那么你就可以考虑使用CAScrollLayer
  • 如果你只是使用户可以滑动,那么你选择UIScrollView会更好。如果想了解更多,可以查看我们的 18部视频教学
  • 如果你要滑动一个非常大的图像,建议使用CATiledLayer(详见下文)

例三: CATextLayer


CATextLayer给纯文本和attributed strings提供了简单而又快速的渲染方法。与UILabel不同的是,CATextLayer中不能使用UIFont,只能是CTFontRef或者CGFontRef。 就像下面的代码块中,CATextLayer可以控制fontfont sizecoloralignmentwrappingtruncation等属性,还有就是动画,代码如下:

// 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的实例,并设置了大小为someView的大小
  • 2、创建了一个文本,并将textLayer.string设置为string
  • 3、创建了一个fontNoteworthy-Light,并将textLayer.font设置为了这个font
  • 4、设置layerwrappedalignmentMode(你可以设置左对齐、右对齐和中间对齐),然后设置它的缩放比例跟屏幕的缩放比例一致,然后加在someViewlayer

所有的层类,并不仅仅是CATextLayer,都是以1:1呈现给我们的。当附属于某一个view的时候,layer默认以当前view的比例因子呈现在屏幕上,你需要给你自己创建的层级设置一个明确的比例因子,否则它的比例因子会默认的被设置为1 如果将它放在一个方方正正的视图上,那么将会向下面这样显示:

Truncation这个属性你可以自己设置看看效果,当你想用省略号截断文本,用这个属性是一个不错的方法。 Truncation默认的是 none,但是可以设置 startendmiddle这三种方式。

demo中你可以控制改变 CATextLayer的很多属性,如下:

例四: AVPlayerLayer


AVPlayerLayerAVFoundation带来了不少方便之处,它拥有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、创建了一个新的layer,并设置了它的frame
  • 2、创建了一个AVPlayer的对象
  • 3、设置当plyer播放完毕以后什么事情也不做,包括播放或者切换下一项(如果有的话)
  • 4、当AVPlayer播放完成以后为它注册一个通知(并设置当这个消息处理结束以后在deinit里面移除掉观察者)
  • 5、设置当按钮点击的时候就出发视频播放的开关和设置按钮的title

请注意,这只是一个供你开始AVPlayerLayer的简单例子,在实际的项目中,通过buttontitle来判断播放状态这种方法通常是不可取的。 上面AVPlayerLayerAVPlayer所创建的将会在视觉上实现AVPlayerItem的第一帧,如下:

AVPlayerLayer有几个附加的属性:

  • videoGravity这个属性用来调整视频的显示尺寸
  • readyForDisplay这个属性用来检查视频是否准备播放

另一方面,AVPlayer有相当多的属性和方法。有一点需要注意的就是速度,比如播放的速率就是从 0 到 1 。0 的意思就是暂停,1 的意思是视频播放的速度是正常的速度(1x)。 然而,设置播放速率也可以控制播放暂停。换句话说,如果想设置暂停的话,就把播放速率设置为 0 。同理,想设置播放的话,就可以把速率设置为 1 。 那么你可能会问,关于快放、慢放和快退是怎么样的体现呢?对于以上这些,AVPlayerLayer都有包含,设置高于一倍的速率就意味着咦当前倍率播放,比如你设置了速率为 2 意味着,播放速度为两倍于当前速率。 正如你可能会认为的那样,速度设置为负数的时候,视频将会以这个速率快退。 当你设置以比正常速率快或者慢播放的之前(向前),然而,AVPlayerItem这个例子中已经提供了适当的方法以验证各种速率:

  • canPlayFastForward()高于 1 的速率
  • canPlaySlowForward()0 ~ 1 的速率
  • canPlayReverse()速率为 -1
  • canPlaySlowReverse()速率为 -1 ~ 0
  • canPlayFastReverse()低于 -1 的速率

大多数视频可以通过不同的速率向前播放,但是很少有可以反向播放的。AVPlayerLayer提供了反向播放的功能:

例五: CAGradientLayer


CAGradientLayer便于两个或多个颜色交融在一起,因而特别适合于背景。你需要指定一个颜色的数组来配置它,此外还需要一个startPoint和一个endPoint来指定CAGradientLayer的开始和结束。 但是请注意,startPoint和一个endPoint并不是一个明确的点。相反,它们是定义在空间坐标系中,然后绘制的时候直接映射到Layer层中。换句话说,x 的坐标为 1 的意思就是该点在Layer的右边缘,当 Y 为 1 的时候意味着该点在layer的底部边缘。 CAGradientLayer有一个type属性,虽然只有kCAGradientLayerAxial这一个选项,但是它可以线性的过度数组中的每一个颜色。 这就意味着,如果你在startPointendPoint之间画一条直线 A ,颜色将会沿着直线 B (假想出来的,垂直于 A )渐变,并且在 B 上所有点的颜色都相同。

另外,你也可以通过一个 0 ~ 1 的数组来控制渐变的位置,并且指定颜色的停止位置,也就是数组中下一个颜色的开始位置。 如果未指定 CAGradientLayer的停止位置,那么它将会默认平均分布,如果分配各个颜色的位置,设置位置的时候必须和颜色的数组相匹配,否则会有不好的事情发生。 下面有一个 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,设置它的framesomeView的大小一致,指定颜色数组,设置startPointendPoint,并将gradient layer添加到图层中。效果看起来应该如下:

是不是看起来非常炫酷,接下来你也可以编写一个炫酷的界面。在 demo中你可以控制 startPointendPoint、颜色和位置,如下:

例六: CAReplicatorLayer


CAReplicatorLayer是按一个指定的数复用一个层组成的,这样在复用的时候就可以做出很多酷炫的效果了。 每个复用的layer都可以有不同的颜色和位置, 如果设置了延迟绘图会使整个layer层的动画延迟。preservesDepth这个属性设置会实现3D或者2D效果,如下:

    // 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的实例,并设置它的framesomeView的大小
  • 2.设置CAReplicatorLayer的复用层数(instanceCount)和延迟绘制的时间。同时还设置了复用层为2DpreservesDepth = false),颜色为白色。
  • 3.为每个实例增加r/g/b 色值,在没有设置的情况下,默认的都是0,并且保留在整个实例中。然而,在这种情况下,这个实例的最初始颜色就被设置成白色了,意味着红绿蓝三种颜色都被设置成 1 了.
 类似资料: