iOS - 圆形径向渐变动画

作者: 豆志昂扬 | 来源:发表于2017-10-27 21:49 被阅读153次

在实现iOS中的动画时,有时候需要自定义圆形径向渐变图层,然后结合CABasicAnimation(keyPath: "自定义属性")定义特定动画。

可iOS SDK中提供的渐变效果只有线性渐变,也就是使用常见的CAGradientLayer。而如果需要实现圆形径向渐变,则需要自定义CALayer。

圆形径向渐变

这些在CSS里只需要几行代码。

background: -webkit-gradient(
  radial, 500 25%, 0, 500 25%, 40, from(blue), to(#eef)
) #f90;

在自定义CALayer时,若其中的属性做到可动画,需要注意一下两点:

  • 重写方法func needsDisplay(forKey key: String) -> Bool
override class func needsDisplay(forKey key: String) -> Bool {
        if key == "自定义属性字符串"  {
            return true
        }
        return super.needsDisplay(forKey: key)
    }
  • 重写方法init(layer: Any)
override init(layer: Any) {
        if let layer = layer as? RadialGradientLayer {
         //假设colors是自定义属性。
            colors = layer.colors
        }
       super.init(layer: layer)
   }

接着来看看完整代码(整理自Stack Overflow)。

import UIKit

enum RadialGradientLayerProperties: String {
    case gradientOrigin
    case gradientRadius
    case colors
    case locations
}

class RadialGradientLayer: CALayer {
    var gradientOrigin = CGPoint() {
        didSet {
            setNeedsDisplay()
        }
    }
    
    var gradientRadius = CGFloat() {
        didSet {
            setNeedsDisplay()
        }
    }
    
    var colors = [CGColor]() {
        didSet {
            setNeedsDisplay()
        }
    }
    
    var locations = [CGFloat]() {
        didSet {
            setNeedsDisplay()
        }
    }
    
    override init(){
        super.init()
        needsDisplayOnBoundsChange = true
    }
    
    required init(coder aDecoder: NSCoder) {
        super.init()
    }
    
//注意点1
    override init(layer: Any) {
        if let layer = layer as? RadialGradientLayer {
            gradientOrigin = layer.gradientOrigin
            gradientRadius = layer.gradientRadius
            colors = layer.colors
            locations = layer.locations
        }
       super.init(layer: layer)
   }
    
//注意点2
    override class func needsDisplay(forKey key: String) -> Bool {
        if key == RadialGradientLayerProperties.gradientOrigin.rawValue || key == RadialGradientLayerProperties.gradientRadius.rawValue || key == RadialGradientLayerProperties.colors.rawValue || key == RadialGradientLayerProperties.locations.rawValue {
            return true
        }
        return super.needsDisplay(forKey: key)
    }
    
    override func action(forKey event: String) -> CAAction? {
        if event == RadialGradientLayerProperties.gradientOrigin.rawValue || event == RadialGradientLayerProperties.gradientRadius.rawValue || event == RadialGradientLayerProperties.colors.rawValue || event == RadialGradientLayerProperties.locations.rawValue {
            let animation = CABasicAnimation(keyPath: event)
            animation.fromValue = self.presentation()?.value(forKey: event)
            return animation
        }
        return super.action(forKey: event)
    }

    override func draw(in ctx: CGContext) {
        let colorSpace = CGColorSpaceCreateDeviceRGB()
        
        if let gradient = CGGradient(colorsSpace: colorSpace, colors: self.colors as CFArray, locations: self.locations) {
            ctx.drawRadialGradient(gradient, startCenter: self.gradientOrigin, startRadius: 0, endCenter: self.gradientOrigin, endRadius: self.gradientRadius, options: .drawsAfterEndLocation)
        }
    }
}

最后来看看如何使用RadialGradientLayer 定义动画,下面的代码是给图片添加遮罩动画:

let gradientLayer = RadialGradientLayer()
gradientLayer.frame = self.image.bounds
gradientLayer.position = CGPoint(x: self.image.bounds.width / 2.0, y: self.image.bounds.height / 2.0)
gradientLayer.locations = [0.1, 0.25]
gradientLayer.gradientOrigin = CGPoint(x: self.image.bounds.width / 2.0, y: self.image.bounds.height / 2.0)
gradientLayer.colors = [UIColor(white: 1.0, alpha: 0).cgColor, UIColor.white.cgColor]
gradientLayer.gradientRadius = min(self.image.bounds.width, self.image.bounds.height)
self.image.layer.addSublayer(gradientLayer)
        
let gradientAnimation = CABasicAnimation(keyPath: "locations")
gradientAnimation.fromValue = gradientLayer.locations
gradientAnimation.toValue = [0.55,0.7]
gradientAnimation.duration = 1.8
gradientAnimation.fillMode = kCAFillModeRemoved
gradientAnimation.isRemovedOnCompletion = true
gradientAnimation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
gradientLayer.add(gradientAnimation, forKey: "maskAnimation")
推荐阅读

iOS 多线程那点事

获取更多内容请关注微信公众号豆志昂扬:

  • 直接添加公众号豆志昂扬
  • 微信扫描下图二维码;

相关文章

网友评论

    本文标题:iOS - 圆形径向渐变动画

    本文链接:https://www.haomeiwen.com/subject/gbudpxtx.html