在实现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")
推荐阅读
获取更多内容请关注微信公众号豆志昂扬:
- 直接添加公众号豆志昂扬;
- 微信扫描下图二维码;
网友评论