Round iOS player seek bar and show scrubber only while dragging

Clip the progress bar with a Capsule for neater edges, and reveal the
scrubber handle only during a drag with a spring zoom animation.
This commit is contained in:
Arkadiusz Fal
2026-05-09 16:54:45 +02:00
parent d49591eaf4
commit 06ae5ac053

View File

@@ -844,6 +844,7 @@ struct PlayerControlsView: View {
Rectangle() Rectangle()
.fill(.red.opacity(0.6)) .fill(.red.opacity(0.6))
.frame(height: 4) .frame(height: 4)
.clipShape(Capsule())
} else { } else {
// Regular VOD progress bar with chapter segments // Regular VOD progress bar with chapter segments
SegmentedProgressBar( SegmentedProgressBar(
@@ -859,16 +860,17 @@ struct PlayerControlsView: View {
sponsorSegments: playerState.sponsorSegments, sponsorSegments: playerState.sponsorSegments,
sponsorBlockSettings: activeLayout.progressBarSettings.sponsorBlockSettings sponsorBlockSettings: activeLayout.progressBarSettings.sponsorBlockSettings
) )
.clipShape(Capsule())
// Scrubber handle - fixed 20x20 frame to prevent layout shift // Scrubber handle - only visible while dragging, zooms in/out
// Hidden when controls are locked to show only the progress bar
Circle() Circle()
.fill(activeLayout.progressBarSettings.playedColor.color) .fill(activeLayout.progressBarSettings.playedColor.color)
.frame(width: 20, height: 20) .frame(width: 20, height: 20)
.scaleEffect(isDragging ? 1.0 : 0.75) .scaleEffect(isDragging ? 1.0 : 0.0)
.offset(x: geometry.size.width * displayProgress - 10, y: 8) .opacity(isDragging ? 1 : 0)
.animation(.easeInOut(duration: 0.1), value: isDragging) .offset(x: geometry.size.width * dragProgress - 10, y: 8)
.opacity(playerState.isControlsLocked ? 0 : 1) .animation(.spring(response: 0.3, dampingFraction: 0.7), value: isDragging)
.allowsHitTesting(false)
} }
} }
.frame(height: 20) .frame(height: 20)
@@ -998,15 +1000,6 @@ struct PlayerControlsView: View {
playerState.chapters.last { $0.startTime <= seekTime } playerState.chapters.last { $0.startTime <= seekTime }
} }
private var displayProgress: Double {
(isDragging || isPendingSeek) ? dragProgress : playerState.progress
}
private var bufferedProgress: Double {
guard playerState.duration > 0 else { return 0 }
return playerState.bufferedTime / playerState.duration
}
// MARK: - Timer Management // MARK: - Timer Management
private func toggleControlsVisibility() { private func toggleControlsVisibility() {