xcode 将渐变颜色应用于使用 UIBezierPath 创建的弧
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/20630653/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Apply gradient color to arc created with UIBezierPath
提问by Granit
I want to create a progress bar that has the form of an arc. The color of the progress bar has to change according to the values.
我想创建一个具有弧形形式的进度条。进度条的颜色必须根据值改变。
I created an arc using UIBezierPath bezierPathWithArcCenter. I used the following code:
我使用UIBezierPath bezierPathWithArcCenter. 我使用了以下代码:
- (void)viewDidLoad
{
[super viewDidLoad];
int radius = 100;
CAShapeLayer *arc = [CAShapeLayer layer];
arc.path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(100, 50) radius:radius startAngle:60.0 endAngle:0.0 clockwise:YES].CGPath;
arc.position = CGPointMake(CGRectGetMidX(self.view.frame)-radius,
CGRectGetMidY(self.view.frame)-radius);
arc.fillColor = [UIColor clearColor].CGColor;
arc.strokeColor = [UIColor purpleColor].CGColor;
arc.lineWidth = 15;
[self.view.layer addSublayer:arc];
CABasicAnimation *drawAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
drawAnimation.duration = 5.0; // "animate over 10 seconds or so.."
drawAnimation.repeatCount = 1.0; // Animate only once..
drawAnimation.removedOnCompletion = NO; // Remain stroked after the animation..
drawAnimation.fromValue = [NSNumber numberWithFloat:0.0f];
drawAnimation.toValue = [NSNumber numberWithFloat:10.0f];
drawAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
[arc addAnimation:drawAnimation forKey:@"drawCircleAnimation"];
}
The result looks like this:
结果如下所示:


My question is: How to apply a gradient to the color if i.e. the value is <= 50%? I also created an UIButton that generates random CGFloat numbers in order to hook it up with the progress bar. Does anyone have an idea how to tackle this?
我的问题是:如果值 <= 50%,如何对颜色应用渐变?我还创建了一个生成随机 CGFloat 数字的 UIButton,以便将它与进度条连接起来。有谁知道如何解决这个问题?
The gradient would look something like this:
渐变看起来像这样:


Any help would be appreciated!
任何帮助,将不胜感激!
Thank you very much.
非常感谢。
Granit
花岗岩
回答by djshiow
You can use a CAGradientLayer to get the gradient effect, and use the CAShapeLayer as a mask.
您可以使用 CAGradientLayer 来获得渐变效果,并使用 CAShapeLayer 作为蒙版。
e.g.:
例如:
- (void)viewDidLoad
{
[super viewDidLoad];
int radius = 100;
CAShapeLayer *arc = [CAShapeLayer layer];
arc.path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(100, 50) radius:radius startAngle:60.0 endAngle:0.0 clockwise:YES].CGPath;
arc.position = CGPointMake(CGRectGetMidX(self.view.frame)-radius,
CGRectGetMidY(self.view.frame)-radius);
arc.fillColor = [UIColor clearColor].CGColor;
arc.strokeColor = [UIColor purpleColor].CGColor;
arc.lineWidth = 15;
CABasicAnimation *drawAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
drawAnimation.duration = 5.0; // "animate over 10 seconds or so.."
drawAnimation.repeatCount = 1.0; // Animate only once..
drawAnimation.removedOnCompletion = NO; // Remain stroked after the animation..
drawAnimation.fromValue = [NSNumber numberWithFloat:0.0f];
drawAnimation.toValue = [NSNumber numberWithFloat:10.0f];
drawAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
[arc addAnimation:drawAnimation forKey:@"drawCircleAnimation"];
CAGradientLayer *gradientLayer = [CAGradientLayer layer];
gradientLayer.frame = self.view.frame;
gradientLayer.colors = @[(__bridge id)[UIColor redColor].CGColor,(__bridge id)[UIColor blueColor].CGColor ];
gradientLayer.startPoint = CGPointMake(0,0.5);
gradientLayer.endPoint = CGPointMake(1,0.5);
[self.view.layer addSublayer:gradientLayer];
//Using arc as a mask instead of adding it as a sublayer.
//[self.view.layer addSublayer:arc];
gradientLayer.mask = arc;
}
回答by Johnny Rockex
To draw a gradient along a stroke, see this implementation: https://stackoverflow.com/a/43668420/917802
要沿笔划绘制渐变,请参阅此实现:https: //stackoverflow.com/a/43668420/917802
EDIT: In short, create a custom UIView class, add a radial gradient to it by iterating between two colours at increasing angles, e.g. colour1 = 1 degree, colour2 = 2 degrees etc, all the way up to 360. Then apply a donut mask to that. As you change the strokeEnd value of the masking CAShapeLayer, you also rotate the underlying radial gradient. Because they move together it looks like the stroke itself has a gradient.
编辑:简而言之,创建一个自定义 UIView 类,通过在两种颜色之间以递增的角度迭代来添加径向渐变,例如 colour1 = 1 度,color2 = 2 度等,一直到 360。然后应用甜甜圈蒙版到那个。当您更改遮罩 CAShapeLayer 的 strokeEnd 值时,您也会旋转底层径向渐变。因为它们一起移动,所以看起来笔画本身有一个渐变。
回答by m.eldehairy
The Answers given so far are great, but they are a bit complicated, I think the following logic should be much simpler:
到目前为止给出的答案很好,但它们有点复杂,我认为以下逻辑应该简单得多:
- Draw your curve/path of your shape layer .
- Close the path on itself, i.e draw the same curve/path but in reverse.
- Create a CAShapeLayer and set its path to the path in (2).
- Give the shape layer in (3) an arbitrary stroke colour.
- Create a CAGradientLayer.
- Set the gradient layer mask to the shape layer in (4).
- Set the colours of the gradient layer to your desired array.
- Add the gradient layer to your original shapeLayer in (1).
- 绘制形状图层的曲线/路径。
- 关闭自身的路径,即绘制相同的曲线/路径但相反。
- 创建一个 CAShapeLayer 并将其路径设置为(2)中的路径。
- 给 (3) 中的形状图层一个任意的笔触颜色。
- 创建一个 CAGradientLayer。
- 将渐变图层蒙版设置为(4)中的形状图层。
- 将渐变图层的颜色设置为所需的数组。
- 将渐变图层添加到(1)中的原始 shapeLayer。
func draweCurve(fromPoint: CGPoint, toPoint: CGPoint, x: CGFloat) {
// ------- 1 --------
let curveLayer = CAShapeLayer()
curveLayer.contentsScale = UIScreen.main.scale
curveLayer.frame = CGRect(origin: .zero, size: CGSize(width: 100, height: 100))
curveLayer.fillColor = UIColor.red.cgColor
curveLayer.strokeColor = UIColor.blue.cgColor
let path = UIBezierPath()
path.move(to: fromPoint)
let controlPoint1 = CGPoint(x: fromPoint.x + 40 * 0.45, y: fromPoint.y)
let controlPoint2 = CGPoint(x: toPoint.x - 40 * 0.45, y: toPoint.y)
path.addCurve(to: toPoint, controlPoint1: controlPoint1, controlPoint2: controlPoint2)
curveLayer.path = path.cgPath
// ------- 2 --------
// close the path on its self
path.addCurve(to: fromPoint, controlPoint1: controlPoint2, controlPoint2: controlPoint1)
addGradientLayer(to: curveLayer, path: path)
}
private func addGradientLayer(to layer: CALayer, path: UIBezierPath) {
// ------- 3 --------
let gradientMask = CAShapeLayer()
gradientMask.contentsScale = UIScreen.main.scale
// ------- 4 --------
gradientMask.strokeColor = UIColor.white.cgColor
gradientMask.path = path.cgPath
// ------- 5 --------
let gradientLayer = CAGradientLayer()
// ------- 6 --------
gradientLayer.mask = gradientMask
gradientLayer.frame = layer.frame
gradientLayer.contentsScale = UIScreen.main.scale
// ------- 7 --------
gradientLayer.colors = [UIColor.gray.cgColor, UIColor.green.cgColor]
// ------- 8 --------
layer.addSublayer(gradientLayer)
}

