如何将 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
方法。
假设有实例变量:startColor
、endColor
、radius
、strokeWidth
、mirrored
。
变量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 = [[Spinner alloc] initWithCenter:CGPointMake(75, 125) radius:50 strokeWidth:3 startColor:[UIColor yellowColor] endColor:[UIColor blackColor]]; Spinner *spinner2 = [[Spinner 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?的主要内容,如果未能解决你的问题,请参考以下文章
使用 Monotouch 时如何将 CGColor 转换为 NSObject?
CAShapeLayer shadowPath 动画。如何防止动画完成时的阴影填充?
如何将 CAGradientLayer 添加到 CAShapeLayer?