如何在SwiftUI中优雅实现圆周12等分段的绘制?
更优雅的SwiftUI实现12段等分圆周方案
针对你当前用12个重复Circle视图实现分段圆周的方式,这里提供三种更简洁、可维护性更强的实现方案:
方案一:用ForEach简化重复代码
将重复的Circle视图通过循环生成,把关键参数抽为常量,方便后续修改:
struct SegmentedCircleView: View { private let segmentCount = 12 private let lineWidth: CGFloat = 25 private let gapRatio: CGFloat = 0.001 // 段间间隙占单段的比例 private let segmentRatio = 1.0 / 12 var body: some View { ZStack { ForEach(0..<segmentCount, id: \.self) { index in Circle() .trim( from: (CGFloat(index) * segmentRatio) + gapRatio, to: (CGFloat(index + 1) * segmentRatio) - gapRatio ) .stroke(Color.black, lineWidth: lineWidth) } } .padding(30) } }
优势:代码量大幅减少,参数集中管理,修改段数、间隙、线宽只需调整常量,可读性强。
方案二:自定义Shape绘制(性能最优)
通过自定义Shape直接绘制所有分段圆弧,仅需一个视图实例,性能更出色:
struct SegmentedCircle: Shape { let segmentCount: Int = 12 let gapRatio: CGFloat = 0.001 let lineWidth: CGFloat = 25 func path(in rect: CGRect) -> Path { var path = Path() let radius = min(rect.width, rect.height) / 2 - lineWidth / 2 let center = CGPoint(x: rect.midX, y: rect.midY) let totalAngle = 2 * CGFloat.pi let segmentAngle = totalAngle / CGFloat(segmentCount) let gapAngle = gapRatio * totalAngle for index in 0..<segmentCount { let startAngle = CGFloat(index) * segmentAngle + gapAngle / 2 let endAngle = startAngle + segmentAngle - gapAngle path.addArc( center: center, radius: radius, startAngle: startAngle, endAngle: endAngle, clockwise: false ) } return path.strokedPath(.init(lineWidth: lineWidth, lineCap: .butt)) } } // 使用示例 struct SegmentedCircleView: View { var body: some View { SegmentedCircle() .foregroundColor(.black) .padding(30) } }
优势:仅需一个视图,避免多视图叠加的性能开销,可灵活控制圆弧的绘制细节,比如线帽、间隙的精准度。
方案三:利用StrokeStyle的dashPattern(最简洁)
借助SwiftUI的StrokeStyle的dash参数,直接实现分段效果,代码最精简:
struct SegmentedCircleView: View { private let segmentCount = 12 private let lineWidth: CGFloat = 25 private let gapLength: CGFloat = 2 // 段间间隙的实际长度,可按需调整 var body: some View { GeometryReader { proxy in let diameter = min(proxy.size.width, proxy.size.height) - 30 * 2 - lineWidth let circumference = CGFloat.pi * diameter let segmentLength = (circumference / CGFloat(segmentCount)) - gapLength Circle() .stroke( Color.black, style: StrokeStyle( lineWidth: lineWidth, lineCap: .butt, dash: [segmentLength, gapLength] ) ) } .padding(30) } }
优势:代码极简,仅需一个Circle视图,利用系统原生的虚线实现逻辑,无需手动处理多个圆弧段。
内容的提问来源于stack exchange,提问作者Oleksandr Ivanenko
相关产品推荐
相关产品推荐

