Core Animation 第六章 专用图层(下)

往期回顾:
序章
第一章 - 图层树
第二章 - 寄宿图
第三章 - 图层几何
第四章 - 视觉效果
第五章 - 变换
第六章 专用图层(上)
项目中使用的代码

CAGradientLayer

相信大家或多或少的都遇到过渐变过渡背景的需求,这种时候你完全可以选择让设计为你切一个图片背景,但是为什么不尝试自己来绘制一个呢?CAGradientLayer就是这样一个用来将颜色平稳过渡的图层。
下面我们先来看一个例子再来解释如何使用CAGradientLayer

class CAGradientLayerViewController: UIViewController {
    @IBOutlet weak var containerView: UIView!
    override func viewDidLoad() {
        let gradientLayer = CAGradientLayer()
        gradientLayer.frame = self.containerView.bounds
        self.containerView.layer.addSublayer(gradientLayer)
        gradientLayer.colors = [UIColor.red.cgColor, UIColor.blue.cgColor]
        gradientLayer.startPoint = CGPoint(x: 0, y: 0)
        gradientLayer.endPoint = CGPoint(x: 1, y: 1)
    }
}
Core Animation 第六章 专用图层(下)_第1张图片
红蓝两色对角线渐变

可以看到CAGradientLayer的时候方法非常简单,你只需要规定好渐变的起始点startPoint,终点endPoint已经你希望混合的颜色colorsCoreAnimation就会平稳的过渡这些颜色。

非均匀渐变

大家可能已经注意到了,上面两种颜色的过渡是完全均匀的,但是我们也会遇到不完全变换的情况,这种时候我们就需要用到CAGradientLayer的另一个属性: locations。这个属性用来标记每种颜色变化的范围,locations数组中元素的数量需要跟colors中的属相相同。

Core Animation 第六章 专用图层(下)_第2张图片
非均匀渐变

    override func viewDidLoad() {
        let gradientLayer = CAGradientLayer()
        gradientLayer.frame = self.containerView.bounds
        gradientLayer.colors = [UIColor.red.cgColor, UIColor.yellow.cgColor, UIColor.green.cgColor]
        gradientLayer.locations = [0.0, 0.25, 0.5]
        gradientLayer.startPoint = CGPoint(x: 0, y: 0)
        gradientLayer.endPoint = CGPoint(x: 1, y: 1)
        self.containerView.layer.addSublayer(gradientLayer)
    }

CAReplicatorLayer

重复图层,跟他的名字一样,这个图层主要用来高效的生成许多相思的图层。CAReplicatorLayer在使用的时候需要指定一个instanceCount(指定这个图层需要重复多少次)以及一个instanceTransform(指定每次重复的时候相对于上一次的3D变化效果),下面简单写一个例子。

override func viewDidLoad() {
        super.viewDidLoad()
        let replicatorLayer = CAReplicatorLayer()
        replicatorLayer.frame = containerView.bounds
        self.containerView.layer.addSublayer(replicatorLayer)
        replicatorLayer.instanceCount = 10
        var transform = CATransform3DIdentity
        transform = CATransform3DTranslate(transform, 0, 200, 0)
        transform = CATransform3DRotate(transform, CGFloat(M_PI / 5.0), 0, 0, 1)
        transform = CATransform3DTranslate(transform, 0, -200, 0)
        replicatorLayer.instanceTransform = transform
        replicatorLayer.instanceBlueOffset = -0.1
        replicatorLayer.instanceGreenOffset = -0.1
        let layer = CALayer()
        layer.frame = replicatorLayer.bounds
        layer.backgroundColor = UIColor.white.cgColor
        replicatorLayer.addSublayer(layer)
    }
Core Animation 第六章 专用图层(下)_第3张图片
重复图层示例

CAScrollLayer

我们都知道,当我们想要找的的内容超出了视图的bounds的时候,我们可以使用UIScrollView以及他的子类UITableView或者UICollectionView来实现滚动展示。当然,CoreAnimation也有对应的图层,那就是CAScrollLayer。不过因为他是一个图层,所以CAScrollLayer并不能处理用户的触摸事件并把它转化为滚动事件,自然也不会自己为你处理类似UIScrollViewDecelerating效果以及反弹效果。如果你想使用CAScrollLayer代替UISCrollView,那么你需要通过UIView来处理用户的手势,然后将它体现在CAScrollLayer上面,而且你还需要处理代理等相关信息,所以说就滚动视图而言CAScrollLayer并不能算得上是一个明确的选择。

CATiledLayer

我们常用的加载图片的方式是 UIImage-imageNamed:或者imageWithContentsOfFile:方法,但是这些方法会阻塞主线程,所以在你加载大图的时候你的app可能会出现卡顿,而且如果图片过大还会出现其他的问题,下面引用作者的原话:

所有显示在屏幕上的图片最终都会被转化为OpenGL纹理,同时OpenGL有一个最大的纹理尺寸(通常是2048x2048,或4096x4096,这个取决于设备型号)。如果你想在单个纹理中显示一个比这大的图,即便图片已经存在于内存中了,你仍然会遇到很大的性能问题,因为Core Animation强制用CPU处理图片而不是更快的GPU。

而CATiledLayer则可以将大图转换为若干小图,然后将他们单独按需加载(瓦片图)。
下面我们先写一个简单的例子将一张4096x4096的皮卡丘分割为若干小图。

let argv = ProcessInfo.processInfo.arguments
guard argv.count >= 2 else {
    assertionFailure("TileCutter arguments: inputfile")
    exit(-1)
}

let inputFile = String.init(cString: argv[1], encoding: String.Encoding.utf8)! as NSString

let titleSize: CGFloat = 256.0

let outputPath = inputFile.deletingPathExtension

let image: NSImage = NSImage(contentsOfFile: inputFile as String)!
var size = image.size
let representations = image.representations
if !representations.isEmpty {
    let representation = representations[0]
    size.width = CGFloat(representation.pixelsWide)
    size.height = CGFloat(representation.pixelsHigh)
}
var rect = NSRect(x: 0.0, y: 0.0, width: size.width, height: size.height)
let imageRef = image.cgImage(forProposedRect: &rect, context: nil, hints: nil)

let rows = Int(ceil(size.height / titleSize))
let cols = Int(ceil(size.width / titleSize))

for y in 0..

运行这个swift的脚本的时候记得在命令行追加自己的图片路径,或者在Xcode中追加scheme参数

Core Animation 第六章 专用图层(下)_第4张图片
修改Edit Scheme

接下来回到我们的CATiledLayer。CATiledLayer可以很好的ScrollView结合在一起。

class CATiledLayerViewController: UIViewController, CALayerDelegate {
    @IBOutlet weak var scrollView: UIScrollView!

    override func viewDidLoad() {
        super.viewDidLoad()
        let tiledLayer = CATiledLayer()
        tiledLayer.frame = CGRect(x: 0, y: 0, width: 4096, height: 4096)
        tiledLayer.delegate = self
        scrollView.layer.addSublayer(tiledLayer)
        scrollView.contentSize = tiledLayer.frame.size
        tiledLayer.setNeedsDisplay()
        tiledLayer.contentsScale = UIScreen.main.scale
    }
    
    func draw(_ layer: CALayer, in ctx: CGContext) {
        let tileLayer = layer as! CATiledLayer
        let bounds = ctx.boundingBoxOfClipPath
        let scale = UIScreen.main.scale
        let x = Int(floor(bounds.origin.x / tileLayer.tileSize.width * scale))
        let y = Int(floor(bounds.origin.y / tileLayer.tileSize.height * scale))
        let imageName = String.init(format: "pica_big_%02i_%02i", x, y)
        let imagePath = Bundle.main.path(forResource: imageName, ofType: "jpg")
        let tileImage = UIImage(contentsOfFile: imagePath!)
        UIGraphicsPushContext(ctx)
        tileImage?.draw(in: bounds)
        UIGraphicsPopContext()
    }
}
Core Animation 第六章 专用图层(下)_第5张图片

CAEmitterLayer

CAEmitterLayer是在iOS5.0中引入的一个高性能的粒子引擎。CAEmitterLayer可以看做是许多CAEmitterCell的容器。下面我们来写一个简单的例子,由于属性EmitterLayerEmitterCell的属性很多,所以这里不再一一赘述,大家可以再文档中查阅。

override func viewDidLoad() {
        super.viewDidLoad()
        let emitter = CAEmitterLayer()
        emitter.frame = containerView.bounds
        containerView.layer.addSublayer(emitter)
        
        emitter.renderMode = kCAEmitterLayerAdditive
        emitter.emitterPosition = CGPoint(x: emitter.frame.size.width / 2.0, y: emitter.frame.size.height / 2.0)
        
        let cell = CAEmitterCell()
        cell.contents = UIImage(named: "Sparkle")?.cgImage
        cell.birthRate = 150
        cell.lifetime = 5.0
        cell.alphaSpeed = -0.3
        cell.velocity = 50
        cell.velocityRange = 50
        cell.emissionRange = CGFloat(M_PI) * 2.0
        emitter.emitterCells = [cell]
    }
Core Animation 第六章 专用图层(下)_第6张图片

小结

书中在后面还提到了CAEAGLLayer和AVPlayerLayer,前者为GLKView中的专用图层,由于本人对于OpenGL了解甚少,所以不再赘述,如果大家感兴趣的话我在单独整理一次,后者虽然是CALyaer的子类但是并不属于CoreAnimation框架所以这里也先跳过了。
另注:本次将原书中的代码转换为了swift,所以也欢迎大家对swift方面多多指教。

你可能感兴趣的:(Core Animation 第六章 专用图层(下))