我可以向 UIBezierPath 添加自定义线帽吗?

Posted

技术标签:

【中文标题】我可以向 UIBezierPath 添加自定义线帽吗?【英文标题】:Can I add a custom line cap to a UIBezierPath? 【发布时间】:2013-12-03 15:29:08 【问题描述】:

我通过创建一个 CAShapeLayer 并给它一个像这样的 Bezier 路径来绘制一条弧线:

self.arcLayer = [CAShapeLayer layer];
UIBezierPath *remainingLayerPath = [UIBezierPath bezierPathWithArcCenter:self.center
                                                                  radius:100
                                                              startAngle:DEGREES_TO_RADIANS(135)
                                                                endAngle:DEGREES_TO_RADIANS(45)
                                                               clockwise:YES];
self.arcLayer.path = remainingLayerPath.CGPath;
self.arcLayer.position = CGPointMake(0,0);

self.arcLayer.fillColor = [UIColor clearColor].CGColor;
self.arcLayer.strokeColor = [UIColor blueColor].CGColor;
self.arcLayer.lineWidth = 15;

这一切都很好,我可以轻松地为弧线从一侧到另一侧设置动画。就目前而言,这为我的线条末端提供了一个非常方正的边缘。我可以用自定义半径(如 3(线宽的三分之一))将这些线帽的边缘变圆吗?我玩过lineCap 属性,但唯一真正的选择似乎是完全方形或圆形,角半径比我想要的大。我还尝试了图层上的cornerRadius 属性,但它似乎没有任何效果(我假设是因为线帽不被视为实际的图层角)。

我只能想到两个真正的选择,我对其中任何一个都不感到兴奋。我可以想出一个完全自定义的 Bezier 路径来跟踪弧的外部,并带有我自定义的圆形边缘。但是,我担心能够以相同的方式为弧设置动画(现在我只是将笔画从 0 设置为 1)。另一种选择是让端盖保持正方形并遮盖角落,但我的理解是遮罩相对昂贵,我计划用这个视图做一些相当密集的动画。

任何建议都会有所帮助。提前致谢。

【问题讨论】:

您不能使用自定义大写创建行。帽的直径始终等于线宽。 cornerRadius 与图层的背景和边框有关,与形状路径无关。 @yurish 那么你将如何达到预期的效果呢? 对不起,这里没有太多帮助。我会尝试第一种方法,但不确定您是否可以成功地为路径设置动画。 【参考方案1】:

我最终通过创建两个完全独立的层来解决这个问题,一个用于左端盖,一个用于右端盖。这是右端盖示例:

self.rightEndCapLayer = [CAShapeLayer layer];
CGRect rightCapRect = CGRectMake(remainingLayerPath.currentPoint.x, remainingLayerPath.currentPoint.y, 0, 0);
rightCapRect = CGRectInset(rightCapRect, self.arcWidth / -2, -1 * endCapRadius);
self.rightEndCapLayer.frame = rightCapRect;
self.rightEndCapLayer.path = [UIBezierPath bezierPathWithRoundedRect:self.rightEndCapLayer.bounds
                                                   byRoundingCorners:UIRectCornerBottomLeft | UIRectCornerBottomRight
                                                         cornerRadii:CGSizeMake(endCapRadius, endCapRadius)].CGPath;
self.rightEndCapLayer.fillColor = self.remainingColor.CGColor;

// Rotate the end cap
self.rightEndCapLayer.anchorPoint = CGPointMake(.5, 0);
self.rightEndCapLayer.transform = CATransform3DMakeRotation(DEGREES_TO_RADIANS(45), 0.0, 0.0, 1.0);

[self.layer addSublayer:self.rightEndCapLayer];

使用贝塞尔路径的当前点可以节省大量数学计算来计算终点应出现的位置。移动锚点还可以使图层不重叠,如果您的弧完全透明,这一点很重要。

这仍然不完全理想,因为动画必须通过多层链接。不过,这比我能想到的替代方案要好。

【讨论】:

这里的 endcapRadius 值是多少 我认为这最终并不重要,因为你只是在做填充,而不是画线。但如果重要的话,我希望它是一个扁平的端盖。

以上是关于我可以向 UIBezierPath 添加自定义线帽吗?的主要内容,如果未能解决你的问题,请参考以下文章

使用 UIBezierPath 向 UIView 添加阴影

如何将圆角添加到 UIBezierPath 自定义矩形?

使用 UIBezierPath 将半径设置为某些角并向自定义 UIButton 添加阴影

无法让 UIBezierPath 应用 linecapstyle / linejoinstyle

向 CAGradientLayer 添加蒙版使 UIBezierPath 消失

swift 用UIBezierPath自定义中间突出的tabbar