You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SwiftUI中如何实现仅顺时针末端圆角的圆形进度条?

实现仅进度末端保留圆角的圆形进度视图

要解决你遇到的「设置lineCap: .round后两端均为圆角,仅需末端(非100%进度时)保留圆角」的问题,可以通过主体平角进度条+末端圆点的组合方式实现,既简单又能精准控制样式:

修改后的完整代码

struct SpeedometerView: View {
    // 可自定义进度值(范围0~1)
    var progress: CGFloat = 0.2
    private let lineWidth: CGFloat = 4
    
    var body: some View {
        ZStack {
            Color.black.edgesIgnoringSafeArea(.all)
            speedProgressView(width: 300)
        }
    }
    
    private func speedProgressView(width: CGFloat) -> some View {
        let dotSize = lineWidth
        ZStack {
            // 1. 主体进度条:两端均为平角
            Circle()
                .trim(from: 0, to: progress)
                .stroke(.red, style: StrokeStyle(lineWidth: lineWidth, lineCap: .butt))
                .shadow(color: .green, radius: 10)
                .shadow(color: .green, radius: 2)
            
            // 2. 末端圆角圆点:仅进度非100%时显示
            if progress < 1.0 {
                Circle()
                    .fill(.red)
                    .shadow(color: .green, radius: 10)
                    .shadow(color: .green, radius: 2)
                    .frame(width: dotSize, height: dotSize)
                    // 偏移到进度条末端的圆周位置
                    .offset(x: (width / 2) - (lineWidth / 2))
                    // 旋转到进度对应的角度(适配trim的进度范围)
                    .rotationEffect(.degrees(progress * 360 - 90))
            }
        }
        .frame(width: width)
        .rotationEffect(.degrees(100))
    }
}

#Preview {
    SpeedometerView()
}

核心思路说明

  1. 主体进度条:将lineCap改为.butt,强制两端为平角,避免起始端出现圆角;
  2. 末端圆点:用一个直径等于线条宽度的圆形,模拟lineCap: .round的末端圆角效果,并且仅在进度未达到100%时显示(避免100%时圆点与起始端重叠);
  3. 位置校准:通过offset将圆点定位到进度条的圆周路径上,再用rotationEffect根据进度值计算旋转角度,确保圆点始终对齐进度末端;
  4. 视觉统一:给圆点添加与主体进度条一致的阴影效果,保证整体样式协调。

这种方法比直接使用lineCap更灵活,能完全满足你对两端样式的差异化需求。

内容的提问来源于stack exchange,提问作者batman

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 11:00:09