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