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

SwiftUI自定义Circle+trim进度条:0.98进度显示为100%的问题

问题分析

你遇到的问题根源在于方形线帽(.square)与较粗线宽(15)在小尺寸Frame下的重叠冲突:

  • 200x200的Frame中,实际绘制的圆半径为(200-15)/2=92.5,当completed=0.98时,剩余缺口的周长约为2π×92.5×0.02≈11.6。
  • 而.square线帽的长度等于线宽(15),这个长度大于缺口周长,导致进度条终点的线帽直接覆盖起点位置,视觉上呈现完全填满的效果。
解决方案

基于必须保留200x200尺寸的要求,提供三种可行方案:

方案1:替换为圆角线帽

将stroke的lineCap从.square改为.round,圆角线帽的延伸长度是线宽的一半(7.5),小于缺口周长,不会覆盖缺口:

struct ContentView: View {
    let remainingTimeStrokeColor: Color = .secondary
    let completedTimeStrokeColor: Color = .blue
    
    @State var completed: Double = 0.98 // 测试值
    
    var body: some View {
        ZStack {
            Circle()
                .stroke(remainingTimeStrokeColor, style: StrokeStyle(lineWidth: 15))
                .frame(width: 200, height: 200)
            Circle()
                .trim(from: 0, to: completed)
                .stroke(completedTimeStrokeColor, style: StrokeStyle(lineWidth: 15, lineCap: .round)) // 修改为圆角线帽
                .rotationEffect(.degrees(-90))
                .frame(width: 200, height: 200)
        }
    }
}

方案2:动态调整Trim终点(保留方形线帽)

如果必须保留.square线帽,可计算线帽对应的周长比例,当进度接近1时自动减小trim的终点值,确保缺口长度大于线帽长度:

struct ContentView: View {
    let remainingTimeStrokeColor: Color = .secondary
    let completedTimeStrokeColor: Color = .blue
    
    @State var completed: Double = 0.98 // 测试值
    private let lineWidth: CGFloat = 15
    private let frameSize: CGFloat = 200
    
    // 计算最大可显示进度,避免线帽覆盖缺口
    private var adjustedCompleted: Double {
        let radius = (frameSize - lineWidth) / 2
        let lineCapRatio = Double(lineWidth) / (2 * .pi * radius)
        return min(completed, 1 - lineCapRatio)
    }
    
    var body: some View {
        ZStack {
            Circle()
                .stroke(remainingTimeStrokeColor, style: StrokeStyle(lineWidth: lineWidth))
                .frame(width: frameSize, height: frameSize)
            Circle()
                .trim(from: 0, to: adjustedCompleted) // 使用调整后的进度值
                .stroke(completedTimeStrokeColor, style: StrokeStyle(lineWidth: lineWidth, lineCap: .square))
                .rotationEffect(.degrees(-90))
                .frame(width: frameSize, height: frameSize)
        }
    }
}

该方案中,adjustedCompleted会自动限制最大可显示进度(约0.974),确保缺口不会被线帽覆盖;若completed小于该阈值,则正常显示原进度。

方案3:缩小绘制路径(进阶)

通过inset(by:)向内收缩Circle的绘制路径,在保持Frame尺寸不变的前提下,增加圆的周长,让缺口长度大于线帽:

struct ContentView: View {
    let remainingTimeStrokeColor: Color = .secondary
    let completedTimeStrokeColor: Color = .blue
    
    @State var completed: Double = 0.98 // 测试值
    private let lineWidth: CGFloat = 15
    
    var body: some View {
        ZStack {
            Circle()
                .inset(by: lineWidth/2) // 向内收缩线宽的一半
                .stroke(remainingTimeStrokeColor, style: StrokeStyle(lineWidth: lineWidth))
                .frame(width: 200, height: 200)
            Circle()
                .inset(by: lineWidth/2)
                .trim(from: 0, to: completed)
                .stroke(completedTimeStrokeColor, style: StrokeStyle(lineWidth: lineWidth, lineCap: .square))
                .rotationEffect(.degrees(-90))
                .frame(width: 200, height: 200)
        }
    }
}

内容的提问来源于stack exchange,提问作者Mahi Al Jawad

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 14:33:34