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

SwiftUI多形状遮罩实现:自定义进度条开发问题

自定义进度条实现方案

针对你需要的「波浪从左显现时背景线条向右侧收缩」的效果,最高效的实现方式是直接动态调整背景线条的显示范围,无需复杂的遮罩嵌套,具体方案如下:

  • 波浪部分:保留你当前的矩形遮罩逻辑,通过progress控制左侧显示区域,实现波浪从左到右逐步显现
  • 背景线条:无需额外遮罩,只需将其宽度设为总宽度的(1 - progress),并设置对齐方式为.trailing,进度增加时,背景线条会自动从左向右收缩,只保留右侧未被波浪覆盖的部分

修改后的完整代码如下:

struct Line: Shape {
    func path(in rect: CGRect) -> Path {
        var path = Path()
        path.move(to: CGPoint(x: 0, y: rect.height/2))
        path.addLine(to: CGPoint(x: rect.width, y: rect.height/2))
        return path
    }
}

struct Wave: Shape {
    var strength: Double
    var frequency: Double
    var phase: Double
    var animatableData: Double {
        get { phase }
        set { self.phase = newValue }
    }
    
    func path(in rect: CGRect) -> Path {
        var path = Path()

        let width = Double(rect.width)
        let height = Double(rect.height)
        let midHeight = height / 2
        let wavelength = width / frequency

        let firstX = 0.0
        let firstRelativeX = firstX / wavelength
        let firstSine = sin(firstRelativeX + phase)
        let firstY = strength * firstSine + midHeight
        path.move(to: CGPoint(x: firstX, y: firstY))

        for x in stride(from: 0.0, through: width, by: 1) {
            let relativeX = x / wavelength
            let sine = sin(relativeX + phase)
            let y = strength * sine + midHeight
            path.addLine(to: CGPoint(x: x, y: y))
        }

        return path
    }
}

struct ContentView: View {
    @State private var phase = 0.0
    @State private var progress = 0.0

    var body: some View {
        VStack {
            GeometryReader(content: { geometry in
                ZStack(alignment: .leading) {
                    // 背景线条:动态调整宽度并靠右对齐
                    Line()
                        .stroke(Color.white.opacity(0.5), lineWidth: 5.0)
                        .frame(width: geometry.size.width * (1 - progress), alignment: .trailing)
                        .frame(maxWidth: .infinity) // 占满父容器,确保对齐生效
                    
                    // 波浪线条:左侧遮罩显示
                    Wave(strength: 10, frequency: 30, phase: phase)
                        .stroke(Color.white, lineWidth: 5)
                        .mask(
                            Rectangle()
                                .frame(width: geometry.size.width * progress)
                        )
                }
            })
            .frame(height: 40) // 给进度条固定高度,避免自适应问题
            
            Slider(value: $progress, in: 0...1)
                .padding()
        }
        .background(Color.black.ignoresSafeArea())
        .onAppear {
            withAnimation(Animation.linear(duration: 2).repeatForever(autoreverses: false)) {
                self.phase = .pi * 2
            }
        }
    }
}

关键改动说明

  1. 背景线条处理:

    • 将Line的宽度设为geometry.size.width * (1 - progress),直接根据进度动态缩短
    • 添加alignment: .trailing让线条靠右显示,配合外层ZStack(alignment: .leading),实现进度增加时线条从左向右收缩
    • 外层加frame(maxWidth: .infinity)确保容器占满宽度,对齐逻辑正常生效
  2. 波浪遮罩简化:

    • 去掉了遮罩里多余的padding(.trailing),直接用Rectangle().frame(width: geometry.size.width * progress)即可实现左侧区域的显示

这个方案性能最优,仅通过简单的视图尺寸调整和对齐实现需求,完全贴合SwiftUI的布局逻辑,动画自然流畅。

内容的提问来源于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 23:43:16