experiment(PodcastPlayerView): add playbackSpeed control (0.75×/1×/1.25×/1.5×/2×) with menu
This commit is contained in:
@@ -42,6 +42,9 @@ final class PodcastPlayerViewModel {
|
|||||||
var duration: Double = 1
|
var duration: Double = 1
|
||||||
var currentArticleUrl: String = ""
|
var currentArticleUrl: String = ""
|
||||||
var currentArticleTitle: String = ""
|
var currentArticleTitle: String = ""
|
||||||
|
var playbackSpeed: Float = 1.0
|
||||||
|
|
||||||
|
static let availableSpeeds: [Float] = [0.75, 1.0, 1.25, 1.5, 2.0]
|
||||||
|
|
||||||
var isGenerating: Bool {
|
var isGenerating: Bool {
|
||||||
switch phase {
|
switch phase {
|
||||||
@@ -135,7 +138,11 @@ final class PodcastPlayerViewModel {
|
|||||||
|
|
||||||
func togglePlayPause() {
|
func togglePlayPause() {
|
||||||
guard let player else { return }
|
guard let player else { return }
|
||||||
if isPlaying { player.pause() } else { player.play() }
|
if isPlaying {
|
||||||
|
player.pause()
|
||||||
|
} else {
|
||||||
|
player.rate = playbackSpeed
|
||||||
|
}
|
||||||
isPlaying.toggle()
|
isPlaying.toggle()
|
||||||
updateNowPlaying()
|
updateNowPlaying()
|
||||||
}
|
}
|
||||||
@@ -154,6 +161,12 @@ final class PodcastPlayerViewModel {
|
|||||||
seek(to: max(currentTime - 15, 0))
|
seek(to: max(currentTime - 15, 0))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func setPlaybackSpeed(_ speed: Float) {
|
||||||
|
playbackSpeed = speed
|
||||||
|
if isPlaying { player?.rate = speed }
|
||||||
|
updateNowPlaying()
|
||||||
|
}
|
||||||
|
|
||||||
private func setupPlayer(url: URL, title: String) {
|
private func setupPlayer(url: URL, title: String) {
|
||||||
currentPodcastTitle = title
|
currentPodcastTitle = title
|
||||||
|
|
||||||
@@ -205,7 +218,7 @@ final class PodcastPlayerViewModel {
|
|||||||
setupRemoteCommands()
|
setupRemoteCommands()
|
||||||
phase = .ready(title: title)
|
phase = .ready(title: title)
|
||||||
updateNowPlaying()
|
updateNowPlaying()
|
||||||
p.play()
|
p.rate = playbackSpeed
|
||||||
isPlaying = true
|
isPlaying = true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -384,6 +397,29 @@ struct PodcastPlayerView: View {
|
|||||||
.foregroundStyle(.primary)
|
.foregroundStyle(.primary)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Menu {
|
||||||
|
ForEach(PodcastPlayerViewModel.availableSpeeds, id: \.self) { speed in
|
||||||
|
Button {
|
||||||
|
vm.setPlaybackSpeed(speed)
|
||||||
|
} label: {
|
||||||
|
let label = speed == 1.0 ? "1× (Normal)" : "\(String(format: "%g", speed))×"
|
||||||
|
if speed == vm.playbackSpeed {
|
||||||
|
Label(label, systemImage: "checkmark")
|
||||||
|
} else {
|
||||||
|
Text(label)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} label: {
|
||||||
|
Text(vm.playbackSpeed == 1.0 ? "1× Speed" : "\(String(format: "%g", vm.playbackSpeed))×")
|
||||||
|
.font(.system(size: 14, weight: .semibold))
|
||||||
|
.foregroundStyle(.secondary)
|
||||||
|
.padding(.horizontal, 14)
|
||||||
|
.padding(.vertical, 7)
|
||||||
|
.background(Color(.systemGray5))
|
||||||
|
.clipShape(Capsule())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user