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 } } } }
关键改动说明
背景线条处理:
- 将
Line的宽度设为geometry.size.width * (1 - progress),直接根据进度动态缩短 - 添加
alignment: .trailing让线条靠右显示,配合外层ZStack(alignment: .leading),实现进度增加时线条从左向右收缩 - 外层加
frame(maxWidth: .infinity)确保容器占满宽度,对齐逻辑正常生效
- 将
波浪遮罩简化:
- 去掉了遮罩里多余的
padding(.trailing),直接用Rectangle().frame(width: geometry.size.width * progress)即可实现左侧区域的显示
- 去掉了遮罩里多余的
这个方案性能最优,仅通过简单的视图尺寸调整和对齐实现需求,完全贴合SwiftUI的布局逻辑,动画自然流畅。
内容的提问来源于stack exchange,提问作者batman
相关产品推荐
相关产品推荐

