如何将 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?的主要内容,如果未能解决你的问题,请参考以下文章

转换一个描边的 CAShapeLayer

使用 Monotouch 时如何将 CGColor 转换为 NSObject?

CAShapeLayer shadowPath 动画。如何防止动画完成时的阴影填充?

如何将 CAGradientLayer 添加到 CAShapeLayer?

如何在不丢失阴影饱和度的情况下将带有阴影的 UIBezierpath 转换为 UIImage

CAShapeLayer 不可见