我知道 CAGradientLayer
目前不支持径向渐变,只有 kCAGradientLayerAxial
选项。
我想要像下面这样的效果:
我查找了这个问题并发现有一种方法解决。但是对我来说解释不清楚。因此,我想知道是否可以使用 CAGradientLayer
绘制径向渐变,如果可以,那么如何做呢?
我知道 CAGradientLayer
目前不支持径向渐变,只有 kCAGradientLayerAxial
选项。
我想要像下面这样的效果:
我查找了这个问题并发现有一种方法解决。但是对我来说解释不清楚。因此,我想知道是否可以使用 CAGradientLayer
绘制径向渐变,如果可以,那么如何做呢?
据我理解,您只需要一个绘制渐变的图层,而CGContextDrawRadialGradient
正好满足这个需求。并且为了重申您所说的,CAGradientLayer
不支持径向渐变,除了可以通过CALayer
子类进行干净地无用替换外我们无法做任何事情。
(注意:渐变绘制代码来源于此处,这不是本答案的主题。)
viewDidLoad
:
GradientLayer *gradientLayer = [[GradientLayer alloc] init];
gradientLayer.frame = self.view.bounds;
[self.view.layer addSublayer:gradientLayer];
CALayer
的子类:
- (instancetype)init
{
self = [super init];
if (self) {
[self setNeedsDisplay];
}
return self;
}
- (void)drawInContext:(CGContextRef)ctx
{
size_t gradLocationsNum = 2;
CGFloat gradLocations[2] = {0.0f, 1.0f};
CGFloat gradColors[8] = {0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.5f};
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGGradientRef gradient = CGGradientCreateWithColorComponents(colorSpace, gradColors, gradLocations, gradLocationsNum);
CGColorSpaceRelease(colorSpace);
CGPoint gradCenter= CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds));
CGFloat gradRadius = MIN(self.bounds.size.width , self.bounds.size.height) ;
CGContextDrawRadialGradient (ctx, gradient, gradCenter, 0, gradCenter, gradRadius, kCGGradientDrawsAfterEndLocation);
CGGradientRelease(gradient);
}
override func drawInContext(ctx: CGContext!) { …
- Andy PoesCALayer
位于subLayers
的底部即可。 - Mazyod这是我使用的Swift3代码:
import UIKit
class RadialGradientLayer: CALayer {
required override init() {
super.init()
needsDisplayOnBoundsChange = true
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
required override init(layer: Any) {
super.init(layer: layer)
}
public var colors = [UIColor.red.cgColor, UIColor.blue.cgColor]
override func draw(in ctx: CGContext) {
ctx.saveGState()
let colorSpace = CGColorSpaceCreateDeviceRGB()
var locations = [CGFloat]()
for i in 0...colors.count-1 {
locations.append(CGFloat(i) / CGFloat(colors.count))
}
let gradient = CGGradient(colorsSpace: colorSpace, colors: colors as CFArray, locations: locations)
let center = CGPoint(x: bounds.width / 2.0, y: bounds.height / 2.0)
let radius = min(bounds.width / 2.0, bounds.height / 2.0)
ctx.drawRadialGradient(gradient!, startCenter: center, startRadius: 0.0, endCenter: center, endRadius: radius, options: CGGradientDrawingOptions(rawValue: 0))
}
}
max
而不是 min
作为半径,并使用 .drawsAfterEndLocation
而不是 CGGradientDrawingOptions(rawValue: 0)
,因为这样会得到更令人愉悦的结果(在我看来)。 - atomoilCALayer
:view.layer.insertSublayer(gradientLayer, below: view.layer)
。 - Chintan Shah我不知道什么时候开始,但现在你可以将 CAGradientLayer
的 type
改为 kCAGradientLayerRadial
,它可以正常工作。
从理论上讲,核心动画的性能比核心图形更好。
一些示例代码:
class View : UIView {
let gradientLayer = CAGradientLayer()
override init(frame: CGRect) {
super.init(frame: frame)
gradientLayer.startPoint = CGPoint(x: 0.5, y: 0.5)
gradientLayer.endPoint = CGPoint(x: 1, y: 1)
gradientLayer.locations = [
NSNumber(value: 0.6),
NSNumber(value: 0.8)
]
gradientLayer.type = .radial
gradientLayer.colors = [
UIColor.green.cgColor,
UIColor.purple.cgColor,
UIColor.orange.cgColor,
UIColor.red.cgColor
]
self.layer.addSublayer(gradientLayer)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func layoutSublayers(of layer: CALayer) {
assert(layer === self.layer)
gradientLayer.frame = layer.bounds
}
}
Swift 5
import UIKit
final class RadialGradientLayer: CALayer {
let centerColor: CGColor
let edgeColor: CGColor
init(centerColor: UIColor, edgeColor: UIColor) {
self.centerColor = centerColor.cgColor
self.edgeColor = edgeColor.cgColor
super.init()
needsDisplayOnBoundsChange = true
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func draw(in ctx: CGContext) {
ctx.saveGState()
let gradient = CGGradient(colorsSpace: CGColorSpaceCreateDeviceRGB(),
colors: [centerColor, edgeColor] as CFArray,
locations: [0.0, 1.0])
let gradCenter = CGPoint(x: bounds.midX, y: bounds.midY)
let gradRadius = min(bounds.width, bounds.height)
ctx.drawRadialGradient(gradient!,
startCenter: gradCenter,
startRadius: 0.0,
endCenter: gradCenter,
endRadius: gradRadius,
options: .drawsAfterEndLocation)
}
}
CGContextDrawRadialGradient
?您可以始终渲染到图像,然后将其分配给CALayer
的内容。使用CALayer
的renderInContext:
也是一种选择,但最终都会导致CGContextDrawRadialGradient
。 - Mazyod