绘制动态圆形UIBezierPath

17

我正在做一个项目,其中我正在根据设置的进度动画显示UIBezierPath。 BezierPath呈圆形并位于UIView中,目前在drawRect中使用CADisplayLink进行动画。简单地说,基于设置的进度x,路径应该径向展开(如果 x比之前大)或收缩(如果x比之前小)。

self.drawProgress = (self.displayLink.timestamp - self.startTime)/DURATION;

CGFloat startAngle = -(float)M_PI_2;
CGFloat stopAngle = ((self.x * 2*(float)M_PI) + startAngle);
CGFloat currentEndAngle = ((self.oldX * 2*(float)M_PI) + startAngle);
CGFloat endAngle = currentEndAngle-((currentEndAngle-stopAngle)*drawProgress);

UIBezierPath *guideCirclePath = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:startAngle endAngle:endAngle clockwise:YES];

如果自上次更新以来x缩小,就会发生这种情况。我目前遇到的问题有:

  1. 始终以45º开始绘制形状(除非我旋转视图)。我没有找到任何改变这个的方法,即使将startAngle设置为-45º也没有什么区别,因为它总是“弹回”到45º。有什么办法可以解决这个问题,还是我必须采用其他绘图方法?
  2. 是否有其他方式可以对这些东西进行动画处理?我读了很多关于使用CAShapeLayer的文章,但我并没有完全理解使用这两种方法的实际区别(在优缺点方面)。如果有人能澄清一下,我将非常感激!

更新:我已将代码迁移到CAShapeLayer,但现在我面临另一个问题。最好用这张图片来描述:

进入图像描述

发生的情况是,当层应该缩小时,薄外线仍然存在(无论移动方向如何)。当条形缩小时,除非我明确地在其上创建一个新的白色形状,否则1-x的增量不会被移除。下面是此代码。有什么想法吗?

UIBezierPath *circlePath = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:startAngle endAngle:stopAngle clockwise:YES];
CAShapeLayer *circle = [CAShapeLayer layer];
circle.path = [circlePath CGPath];
circle.strokeStart = 0;
circle.strokeEnd = 1.0*self.progress;

// Colour and other customizations here.

if (self.progress > self.oldProgress) {
    drawAnimation.fromValue = @(1.0*self.oldProgress);
    drawAnimation.toValue = @(circle.strokeEnd);
} else { 
    drawAnimation.fromValue = @(1.0*self.oldProgress);
    drawAnimation.toValue = @(1.0*self.progress);
    circle.strokeEnd = 1.0*self.progress;
}

drawAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; //kCAMediaTimingFunctionEaseIn
[circle addAnimation:drawAnimation forKey:@"strokeEnd"];

更新2:我已经解决了大部分其他错误。事实证明,整个动画问题只是我太傻了,并且过度复杂化(更不用说到处乘以1了,对吧?)。我制作了一个gif图来展示我无法解决的错误:

radial control glitch

有任何想法吗?

更新3:(已经解决)。我通过调用

[self.layer.sublayers makeObjectsPerformSelector:@selector(removeFromSuperlayer)];

现在一切都按照它应该的方式运行。感谢所有的帮助!


你是否想要做类似于这个(通过增加一个线段的角度从0到360度来创建一个完整的圆):https://dev59.com/qWsz5IYBdhLWcg3wTl4T#8021051? - David Rönnqvist
我已经迁移到使用CAShapeLayer+CABasicAnimation,因为这似乎是更明智的选择,部分原因是能够使用Duncan推荐的属性。 - hav
寻找一个好的 Swift 解决方案。 - hugocarlmartin
1个回答

11

CAShapeLayer的使用更加简单和清晰。原因是CAShapeLayer包含属性strokeStart和strokeEnd。这些值的范围从0(路径的起点)到1(路径的终点),并且可以进行动画处理。

通过改变它们,您可以轻松地绘制圆形的任何弧(或者任意路径的任何部分)。这些属性是可动画的,因此您可以创建一个增长/缩小的饼图或环形图的部分的动画。比在drawRect中实现代码要容易得多,并且更加高效。


1
这实际上大大简化了事情,我已经将代码迁移到基于CAShapeLayer和CABasicAnimation的解决方案上,所以谢谢!然而,现在我遇到了另一个奇怪的问题... - hav
1
那是什么奇怪的事情? - Duncan C

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接