如何将 CAShapeLayer 转换为 CAGradientLayer?

Posted

技术标签:

【中文标题】如何将 CAShapeLayer 转换为 CAGradientLayer?【英文标题】:How to Convert CAShapeLayer to CAGradientLayer? 【发布时间】:2015-11-28 05:26:29 【问题描述】:

我制作 CAShapeLayer 的代码是

UIBezierPath *circle = [UIBezierPath bezierPathWithArcCenter:CGPointMake(75, 125)
                                                          radius:50
                                                      startAngle:0
                                                        endAngle:1.8 * M_PI
                                                       clockwise:YES];

    CAShapeLayer *circleLayer = [CAShapeLayer layer];
    [circleLayer setFrame:CGRectMake(200 - 50, 300 - 50, 100, 100)];
    circleLayer.path   = circle.CGPath;
    circleLayer.bounds = CGPathGetBoundingBox(circle.CGPath);
    circleLayer.strokeColor = [UIColor colorWithRed:0.4 green:1.0 blue:0.2 alpha:0.5].CGColor;
    [circleLayer setFillColor:[UIColor clearColor].CGColor];
    circleLayer.lineWidth   = 3.0;

    if ([circleLayer animationForKey:@"SpinAnimation"] == nil) 
        CABasicAnimation* animation = [CABasicAnimation animationWithKeyPath:@"transform.rotation"];
        animation.fromValue = [NSNumber numberWithFloat:0.0f];
        animation.toValue = [NSNumber numberWithFloat: 2 * M_PI];
        animation.duration = 2.0f;
        animation.repeatCount = INFINITY;
        animation.removedOnCompletion = NO;
        [circleLayer addAnimation:animation forKey:@"SpinAnimation"];
    
    [self.view.layer addSublayer:circleLayer];

我想制作 CAGradientLayer 而不是 CAShapeLayer。

原因:我需要在图层中使用渐变色,这在 CAShapeLayer 中是不可能的。

我想在 Cirle 上使用黄色到黑色的渐变。

图片:

在我需要的输出中,颜色是从背景图像中获取的,并在末尾添加一些黑色,或者在图层中添加不透明度 0。

任何想法,建议。

谢谢。

【问题讨论】:

视图的背景颜色是否固定?我的意思是,背景颜色是你已经知道的某种颜色(比如白色)。 是的,View的背景颜色是固定的@ChengYuHsu 【参考方案1】:

一个快速而肮脏的技巧是添加一个渐变层作为子层,然后添加一些背景颜色与超级视图相同的层(在原始问题中,它是白色的)。

例如:

    添加渐变层(并设置正确的cornerRadius)。

    在渐变层的中心添加一个白色背景的较小层(这将创建一个)。

    用白色背景创建一小段圆弧。

但是,这可能不适合带有背景图像的视图。 为了解决这个问题,您可以创建UIView 的自定义子类并覆盖drawRect 方法。

假设有实例变量:startColorendColorradiusstrokeWidthmirrored

变量mirrored用于控制间隙的位置(切断左侧或右侧)和动画的旋转方向(顺时针或逆时针)。

- (void)drawRect:(CGRect)rect

    CGContextRef ctx = UIGraphicsGetCurrentContext();

    CGContextSetBlendMode(ctx, kCGBlendModeNormal);

    CGFloat gradientColors[4 * 2];

    extractColorComponents(_startColor, gradientColors, 0);
    extractColorComponents(_endColor, gradientColors, 4);


    CGColorSpaceRef baseSpace = CGColorSpaceCreateDeviceRGB();
    CGGradientRef gradient = CGGradientCreateWithColorComponents(baseSpace, gradientColors, NULL, 2);
    CGColorSpaceRelease(baseSpace);
    baseSpace = nil;

    CGPoint startPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMinY(rect));
    CGPoint endPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMaxY(rect));
    CGContextDrawLinearGradient(ctx, gradient, startPoint, endPoint, 0);
    CGGradientRelease(gradient);
    gradient = nil;

    // fill 'clear' color
    CGContextSetFillColorWithColor(ctx, [UIColor clearColor].CGColor);
    CGContextSetStrokeColorWithColor(ctx, [UIColor clearColor].CGColor);

    CGContextSetBlendMode(ctx, kCGBlendModeClear);

    CGFloat innerCircleRadius = _radius - _strokeWidth;

    // fill an 'empty' hole inside the gradient part
    CGContextFillEllipseInRect(ctx, (CGRect)
        _strokeWidth, _strokeWidth,
        innerCircleRadius * 2, innerCircleRadius * 2
    );


    // fill an 'empty' segment of arc
    CGFloat startAngle = _mirrored ? (0.9 * M_PI) : (-0.1 * M_PI);
    CGFloat endAngle = startAngle + 0.2 * M_PI;
    UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:(CGPoint)_radius, _radius
                                                        radius:_radius - _strokeWidth * 0.5
                                                    startAngle:startAngle
                                                      endAngle:endAngle
                                                     clockwise:YES];
    path.lineWidth = _strokeWidth;
    CGContextAddPath(ctx, path.CGPath);
    CGContextSetLineWidth(ctx, _strokeWidth + 2);
    CGContextStrokePath(ctx);

    CGContextSetBlendMode(ctx, kCGBlendModeNormal);

这是视图的complete implementation。

最终结果:

两个环:

您可以在viewDidLoad 或其他地方添加两个环,使用以下代码来达到预期的效果:

- (void)viewDidLoad 

    [super viewDidLoad];

    UIColor *startColor = [UIColor colorWithHue:0.02 saturation:0.74 brightness:0.91 alpha:1];
    UIColor *endColor = [UIColor colorWithHue:0.57 saturation:0.76 brightness:0.86 alpha:1];

    CGPoint center = CGPointMake(160, 200);

    Spinner *outerSpinner = [[Spinner alloc] initWithCenter:center
                                                     radius:50
                                                strokeWidth:3
                                                 startColor:startColor
                                                   endColor:endColor
                                                   mirrored:NO];

    Spinner *innerSpinner = [[Spinner alloc] initWithCenter:center
                                                     radius:40
                                                strokeWidth:3
                                                 startColor:startColor
                                                   endColor:endColor
                                                   mirrored:YES];


    [self.view addSubview:outerSpinner];
    [self.view addSubview:innerSpinner];

    [outerSpinner startAnimating];
    [innerSpinner startAnimating];


结果:

【讨论】:

亲爱的,我做到了。我现在有另一个问题。我不能做两个。 :) 如果你看到我的图片,有两个圆形,一个是顺时针的,另一个是逆时针的 :) 如果你能帮我做两个,一个是顺时针的,一个是逆时针的。由于我们有完整的圆,我们可以看到它的笔划,那么我们如何用两个圆来实现呢? 您可以创建 2 个具有不同半径的 Spinner 视图实例,并为每个视图添加适当的动画。 但是亲爱的,它会重叠颜色:) 由于两者在同一位置,一个圆比另一个小 无法添加两个圈子。给 EXC_BAD_ACCESS。 Spinner *spinner = [[Spin​​ner alloc] initWithCenter:CGPointMake(75, 125) radius:50 strokeWidth:3 startColor:[UIColor yellowColor] endColor:[UIColor blackColor]]; Spinner *spinner2 = [[Spin​​ner alloc] initWithCenter:CGPointMake(75, 125) radius:40 strokeWidth:3 startColor:[UIColor yellowColor] endColor:[UIColor blackColor]]; [self.view addSubview:spinner]; [self.view addSubview:spinner2];【参考方案2】:

CALayer 上的 mask 属性使用遮罩层的 alpha 分量来确定什么应该可见和不可见。由于您的两种颜色(黑色和白色)都是完全不透明的(不透明),因此蒙版无效。要解决此问题,您应该将其中一种颜色更改为清晰的颜色:

gradient.colors = [NSArray arrayWithObjects:(id)[[UIColor blackColor] CGColor], 
                                            (id)[[UIColor clearColor] CGColor], nil];

编辑

class Colors 
let colorTop = UIColor(red: 192.0/255.0, green: 38.0/255.0, blue: 42.0/255.0, alpha: 1.0).CGColor
let colorBottom = UIColor(red: 35.0/255.0, green: 2.0/255.0, blue: 2.0/255.0, alpha: 1.0).CGColor

let gl: CAGradientLayer

init() 
    gl = CAGradientLayer()
    gl.colors = [ colorTop, colorBottom]
    gl.locations = [ 0.0, 1.0]


【讨论】:

哦,太好了,但你能在你的代码中解释一下什么是渐变! 那么,你能找到任何具有 CAGradientLayer 的单个对象吗?我想你没有得到我的问题。很简单,我需要改变 CAShapeLayer 的笔触颜色,或者从一开始就使用 CAGradientLayer。

以上是关于如何将 CAShapeLayer 转换为 CAGradientLayer?的主要内容,如果未能解决你的问题,请参考以下文章