SwiftUI 音频播放器:AVPlayer 在线播放 + 播放进度条 + 音量调节 + 后台播放
在 iOS 应用中,如果你想实现一个简洁优雅的音乐播放器组件,Apple 的 AVPlayer
是一个非常可靠的工具。本文将带你用 SwiftUI 构建一个支持:
✅ 在线音频播放
✅ 实时播放进度条
✅ 音量调节
✅ 后台播放(锁屏仍可播放)
实现效果
下图展示了本文要实现的 SwiftUI 播放器界面:
项目结构预览
我们将实现以下三个主要组件:
PlayerViewModel
: 音频播放逻辑 + 状态管理PlayerView
: 播放器界面(播放按钮、进度条、音量调节)ContentView
: 加载一个在线音频 URL 进行播放
1. 创建 PlayerViewModel:播放逻辑核心
我们使用 ObservableObject
管理 AVPlayer 播放器状态,支持实时监听播放进度和音量。
import Foundation
import AVFoundation
import Combine
import SwiftUIclass PlayerViewModel: ObservableObject {@Published var player: AVPlayer // 播放器对象@Published var isPlaying = false // 是否正在播放@Published var currentTime: Double = 0 // 当前播放时间(秒)@Published var duration: Double = 1 // 音频总时长(秒)@Published var volume: Float = 0.5 // 音量(0~1)private var timeObserverToken: Any? // 播放进度监听private var cancellables = Set<AnyCancellable>() // Combine 订阅集合// 初始化 AVPlayer 并配置监听init(url: URL) {self.player = AVPlayer(url: url)setupObserver() // 监听播放进度setupVolume() // 音量变化绑定configureAudioSession() // 配置后台播放}// 销毁时移除观察者deinit {if let token = timeObserverToken {player.removeTimeObserver(token)}}/// 设置播放进度监听private func setupObserver() {timeObserverToken = player.addPeriodicTimeObserver(forInterval: CMTime(seconds: 0.5, preferredTimescale: 600),queue: .main) { [weak self] time inguard let self = self,let currentItem = self.player.currentItem else { return }self.currentTime = time.secondsself.duration = currentItem.duration.seconds}}/// 音量绑定监听private func setupVolume() {player.volume = volume$volume.sink { [weak self] newVolume inself?.player.volume = newVolume}.store(in: &cancellables)}/// 播放 / 暂停 控制func togglePlayPause() {if isPlaying {player.pause()} else {player.play()}isPlaying.toggle()}/// 拖动进度条跳转到指定时间func seek(to time: Double) {player.seek(to: CMTime(seconds: time, preferredTimescale: 600))}/// 开启后台播放能力private func configureAudioSession() {do {try AVAudioSession.sharedInstance().setCategory(.playback, mode: .default)try AVAudioSession.sharedInstance().setActive(true)} catch {print("Failed to configure audio session: \(error)")}}
}
2. 构建 SwiftUI 播放界面 PlayerView
这个界面包含播放按钮、音量滑块和进度条。
struct PlayerView: View {@StateObject private var viewModel: PlayerViewModel// 接收外部传入的音频 URLinit(url: URL) {_viewModel = StateObject(wrappedValue: PlayerViewModel(url: url))}var body: some View {VStack(spacing: 20) {// 播放 / 暂停 按钮Button(action: {viewModel.togglePlayPause()}) {Image(systemName: viewModel.isPlaying ? "pause.circle.fill" : "play.circle.fill").resizable().frame(width: 80, height: 80)}// 音量调节HStack {Text("音量: \(Int(viewModel.volume * 100))%")Slider(value: Binding(get: { viewModel.volume },set: { viewModel.volume = $0 }),in: 0...1).accentColor(.green).padding(.horizontal)}// 播放进度条HStack {Text("\(formatTime(viewModel.currentTime))")Slider(value: $viewModel.currentTime,in: 0...viewModel.duration,onEditingChanged: { isEditing inif !isEditing {viewModel.seek(to: viewModel.currentTime)}})Text("\(formatTime(viewModel.duration))")}}.padding()}/// 时间格式化工具:转成 mm:ss 格式private func formatTime(_ time: Double) -> String {let minutes = Int(time) / 60let seconds = Int(time) % 60return String(format: "%02d:%02d", minutes, seconds)}
}
3. 在 ContentView 中加载播放器
我们选用一首可用的在线音频地址示例:
struct ContentView: View {let audioURL = URL(string: "https://er-sycdn.kuwo.cn/307531268bf6fddfb4adb34fe9d4c3a3/6805fabb/resource/30106/trackmedia/M500004CJfjo2EGyPZ.mp3")!var body: some View {PlayerView(url: audioURL)}
}
4. 支持后台播放说明
通过设置:
try AVAudioSession.sharedInstance().setCategory(.playback)
在 Info.plist
中添加:
<key>UIBackgroundModes</key>
<array><string>audio</string>
</array>
即可支持后台音频播放(包括锁屏继续播放)功能。