在不让自己发疯的情况下做到这一点的关键是方法addArc(tangent1End:tangent2End:radius:transform:)。这为现有的CGMutablePath 添加了一条弧线。该方法从路径的当前点开始。您指定一个点 tangent1End,这是您要为其绘制圆角的顶点,以及另一个点 tangent2End,它是您 pologon 中的下一个顶点。
要绘制具有可变数量顶点的多边形,我们使用点数组。
为了使所有角都变圆,您必须将路径的起点设置为多边形直线段上某处的某个点,然后在最后返回该点。计算2个点的中点很容易:
let midpoint = CGPoint(x: (point1.x + point2.x)/2, y: (point1.y + point2.y)/2 )
所以我们将路径的当前点移动到顶点数组中第一个点和最后一个点之间的中点作为起点:
let midpoint = CGPoint(x: (first.point.x + last.point.x) / 2, y: (first.point.y + last.point.y) / 2 )
path.move(to: midpoint)
然后,对于多边形中的每个顶点,我们要么只画一条线到那个点(用于尖角),要么使用美妙、易于使用的addArc(tangent1End:tangent2End:radius:transform:) 来绘制一条线段,以圆弧,围绕那个顶点。
我将创建 UIView 的子类 RoundedCornerPolygonView。
class RoundedCornerPolygonView: UIView {
}
它将自己设置为使其内容层为 CAShapeLayer。为此,您只需将类 var layerClass 添加到您的自定义 UIView 子类:
// This class var causes the view's base layer to be a CAShapeLayer.
class override var layerClass: AnyClass {
return CAShapeLayer.self
}
为了跟踪点数组,以及哪些点应该是圆的,哪些应该是平滑的,我们将定义一个结构PolygonPoint:
struct PolygonPoint {
let point: CGPoint
let isRounded: Bool
}
我们会给我们的班级一个PolygonPoints 的数组:
public var points = [PolygonPoint]()
并添加一个 didSet 方法来更新我们的形状层的路径,如果它改变了:
public var points = [PolygonPoint]() {
didSet {
guard points.count >= 3 else {
print("Polygons must have at least 3 sides.")
return
}
buildPolygon()
}
}
这是从上面的PolygonPoints 数组构建多边形路径的代码:
/// Rebuild our polygon's path and install it into our shape layer.
private func buildPolygon() {
guard points.count >= 3 else { return }
drawPoints() // Draw each vertex into another layer if requested.
let first = points.first!
let last = points.last!
let path = CGMutablePath()
// Start at the midpoint between the first and last vertex in our polygon
// (Since that will always be in the middle of a straight line segment.)
let midpoint = CGPoint(x: (first.point.x + last.point.x) / 2, y: (first.point.y + last.point.y) / 2 )
path.move(to: midpoint)
//Loop through the points in our polygon.
for (index, point) in points.enumerated() {
// If this vertex is not rounded, just draw a line to it.
if !point.isRounded {
path.addLine(to: point.point)
} else {
//Draw an arc from the previous vertex (the current point), around this vertex, and pointing to the next vertex.
let nextIndex = (index+1) % points.count
let nextPoint = points[nextIndex]
path.addArc(tangent1End: point.point, tangent2End: nextPoint.point, radius: cornerRadius)
}
}
// Close the path by drawing a line from the last vertex/corner to the midpoint between the last and first point
path.addLine(to: midpoint)
// install the path into our (shape) layer
let layer = self.layer as! CAShapeLayer
layer.path = path
}
我在 Github 上创建了一个示例项目,它实现了上面定义的 RoundedCornerPolygonView 类,并允许您在运行时选择哪些角应该是圆角或平滑的。
该项目名为“RoundedCornerPolygon”。 (链接)
它的视图控制器类中也有代码,该代码从顶点数组 (CGPoints) 开始。它使用顶点数组中每个顶点的开关以及该顶点的标签来填充垂直堆栈视图。然后它构建一个PolygonPoints 数组并将其安装到RoundedCornerPolygonView。
如果用户切换任何开关,它会重建PolygonPoints 的数组并将它们传递给RoundedCornerPolygonView,后者会重新绘制自身。
屏幕如下所示: