如何在SwiftUI中创建WedgePerimeter形状?单侧宽度无法缩小
问题描述
我不清楚这个几何形状的具体名称,尝试用SwiftUI的Shape组件复现它,但没法把其中一侧的宽度设置得更小。效果示意图为一段弧形条带,靠近起始角的一侧宽度较宽,靠近结束角的一侧宽度收窄。
当前实现代码
struct WedgePerimeter: Shape { var startAngle: Angle var endAngle: Angle var lineMaxWidth: CGFloat var lineMinWidth: CGFloat var animatableData: AnimatablePair<AnimatablePair<Double, Double>, AnimatablePair<Double, Double>> { get { AnimatablePair( AnimatablePair(startAngle.radians, endAngle.radians), AnimatablePair(Double(lineMaxWidth), Double(lineMinWidth)) ) } set { startAngle = Angle(radians: newValue.first.first) endAngle = Angle(radians: newValue.first.second) lineMaxWidth = CGFloat(newValue.second.first) lineMinWidth = CGFloat(newValue.second.second) } } func path(in rect: CGRect) -> Path { var path = Path() let center = CGPoint(x: rect.midX, y: rect.midY) let radius = min(rect.width, rect.height) / 2 // Outer Arc path.addArc(center: center, radius: radius, startAngle: startAngle, endAngle: endAngle, clockwise: false) // Line to the inner radius at endAngle path.addLine(to: CGPoint(x: center.x + (radius - 0) * CGFloat(cos(endAngle.radians)), y: center.y + (radius - 0) * CGFloat(sin(endAngle.radians)))) // Inner Arc path.addArc(center: center, radius: radius - lineMinWidth, startAngle: endAngle, endAngle: startAngle, clockwise: true) // Line to the outer radius at startAngle path.addLine(to: CGPoint(x: center.x + radius * CGFloat(cos(startAngle.radians)), y: center.y + radius * CGFloat(sin(startAngle.radians)))) //path.closeSubpath() return path } }
问题分析与修正代码
原代码的核心问题是:结束角位置的直线错误连接到了外半径(radius - 0),同时内弧使用固定半径,无法实现宽度从大到小的渐变。下面是修正后的实现:
方案1:结束端收为尖点
如果需要结束端完全收窄成尖点,代码如下:
struct TaperedWedge: Shape { var startAngle: Angle var endAngle: Angle var maxWidth: CGFloat // 起始端的宽度 var animatableData: AnimatablePair<Double, Double> { get { AnimatablePair(startAngle.radians, endAngle.radians) } set { startAngle = Angle(radians: newValue.first) endAngle = Angle(radians: newValue.second) } } func path(in rect: CGRect) -> Path { var path = Path() let center = CGPoint(x: rect.midX, y: rect.midY) let outerRadius = min(rect.width, rect.height) / 2 let innerRadiusStart = outerRadius - maxWidth // 绘制外弧:从起始角到结束角 path.addArc(center: center, radius: outerRadius, startAngle: startAngle, endAngle: endAngle, clockwise: false) // 绘制尖点连接线:从外弧终点指向圆心 path.addLine(to: center) // 绘制内边缘:从圆心回到起始端内边缘 path.addLine(to: CGPoint( x: center.x + innerRadiusStart * CGFloat(cos(startAngle.radians)), y: center.y + innerRadiusStart * CGFloat(sin(startAngle.radians)) )) // 闭合路径 path.closeSubpath() return path } }
方案2:结束端保留最小宽度
如果需要结束端保留指定的最小宽度,可增加minWidth参数:
struct TaperedWedge: Shape { var startAngle: Angle var endAngle: Angle var maxWidth: CGFloat // 起始端宽度 var minWidth: CGFloat // 结束端最小宽度 var animatableData: AnimatablePair<AnimatablePair<Double, Double>, AnimatablePair<Double, Double>> { get { AnimatablePair( AnimatablePair(startAngle.radians, endAngle.radians), AnimatablePair(Double(maxWidth), Double(minWidth)) ) } set { startAngle = Angle(radians: newValue.first.first) endAngle = Angle(radians: newValue.first.second) maxWidth = CGFloat(newValue.second.first) minWidth = CGFloat(newValue.second.second) } } func path(in rect: CGRect) -> Path { var path = Path() let center = CGPoint(x: rect.midX, y: rect.midY) let outerRadius = min(rect.width, rect.height) / 2 let innerRadiusStart = outerRadius - maxWidth let innerRadiusEnd = outerRadius - minWidth // 绘制外弧 path.addArc(center: center, radius: outerRadius, startAngle: startAngle, endAngle: endAngle, clockwise: false) // 绘制结束端宽度线:从外弧终点到内弧终点 path.addLine(to: CGPoint( x: center.x + innerRadiusEnd * CGFloat(cos(endAngle.radians)), y: center.y + innerRadiusEnd * CGFloat(sin(endAngle.radians)) )) // 绘制内边缘:连接结束端内点与起始端内点 path.addLine(to: CGPoint( x: center.x + innerRadiusStart * CGFloat(cos(startAngle.radians)), y: center.y + innerRadiusStart * CGFloat(sin(startAngle.radians)) )) // 闭合路径 path.closeSubpath() return path } }
修正说明
- 调整了结束端的连接线逻辑,根据设置的宽度参数计算对应内半径位置,实现宽度渐变效果
- 保留了动画支持,确保角度、宽度参数变化时能平滑过渡
- 两种方案可根据需求选择,方案1适合尖尾效果,方案2适合保留最小宽度的场景
内容的提问来源于stack exchange,提问作者Codelaby
相关产品推荐
相关产品推荐

