SwiftUI 与 AVFoundation 相结合,让您可以在应用程序中轻松创建多功能音频播放器。
使用音频播放器,首先要导入 AVFoundation
,这是苹果用于操作音频和视频内容的框架。
import AVFoundation
新建一个 AudioPlayerViewModel 类,充当 SwiftUI 视图和 AVAudioPlayer 实例之间的桥梁。它处理 AVAudioPlayer 的初始化并管理播放状态,如果音频文件丢失或音频播放器无法实例化,该类还会记录相应的错误消息。并且可以控制音频的播放和暂停。
class AudioPlayerViewModel: ObservableObject {
// 声明一个音频播放器,AVAudioPlayer 是用于音频播放的类
var audioPlayer: AVAudioPlayer?
@Published var isPlaying = false
// 初始化:将播放器和音频绑定
init() {
if let sound = Bundle.main.path(forResource: "PocketCyclopsLvl1", ofType: "mp3") {
do {
self.audioPlayer = try AVAudioPlayer(contentsOf: URL(fileURLWithPath: sound))
} catch {
print("AVAudioPlayer could not be instantiated.")
}
} else {
print("Audio file could not be found.")
}
}
// 通过控制器去控制音频的播放暂停
func playOrPause() {
guard let player = audioPlayer else { return }
if player.isPlaying {
player.pause()
isPlaying = false
} else {
player.play()
isPlaying = true
}
}
}
在具体视图中,将 AudioPlayerViewModel
实例化为 @State
,确保该实例在 ContentView
的生命周期内持续存在。
提供一个按钮,点击该按钮时,会调用视图模型上的 playOrPause
方法,从而控制音频播放。按钮内的图像会动态变化以反映当前播放状态(音频暂停时显示播放图标,或音频播放时显示暂停图标)。
struct ContentView: View {
// 创建音频控制器的实例
@State var audioPlayerViewModel = AudioPlayerViewModel()
var body: some View {
VStack {
Button(action: {
audioPlayerViewModel.playOrPause()
}) {
Image(systemName: audioPlayerViewModel.isPlaying ? "pause.circle" : "play.circle")
.resizable()
.frame(width: 64, height: 64)
}
}
}
}
// 首先导入 AVFoundation 框架,其中包括 AVAudioPlayer ,一个用于从文件或内存播放音频的类
import AVFoundation
// ContentView 结构体包括两个用于控制声音播放的 Button 视图和一个用于选择音效的 Picker 视图
struct ContentView: View {
@State private var player: AVAudioPlayer?
@State private var selectedSound: String = "bounce"
let soundNames = ["bounce", "button", "crawler_die", "crawler_jump", "flyerattack", "flyercloseeye", "flyerdie"]
var body: some View {
VStack {
Picker(selection: $selectedSound, label: Text("Select Sound")) {
ForEach(soundNames, id: \\.self) {
Text($0)
}
}
.padding()
Button(action: {
self.playSound()
}) {
Text("Play Sound")
}
}
}
func playSound() {
guard let soundURL = Bundle.main.url(forResource: selectedSound, withExtension: "wav") else {
return
}
do {
player = try AVAudioPlayer(contentsOf: soundURL)
} catch {
print("Failed to load the sound: \\(error)")
}
player?.play()
}
}
VideoPlayer
视图可以从任何 URL(本地或远程)播放电影。
VideoPlayer
来自于 AVKit
框架,因此在尝试之前应该确保导入该框架:
import AVKit
假设您的应用程序包中有 video.mp4 并且想要播放它,可以使用以下代码:
// 尝试在主应用程序包中查找名为“BookTrailer.m4v”的文件
let videoURL: URL? = Bundle.main.url(forResource: "BookTrailer", withExtension: "m4v")
VStack {
if let url = videoURL {
// 调用播放器
VideoPlayer(player: AVPlayer(url: url))
} else {
Text("Video not found")
}
}
.frame(height: 400)
// 如果想播放远程视频,请使用其远程 URL:
VideoPlayer(player:
AVPlayer(
url: URL(string: "<https://yoursite.com/video.mp4>")!
)
)
.frame(height: 400)
如果需要,可以向 VideoPlayer
初始值设定项提供第二个参数闭包,用于添加要在视频上绘制的内容。这将绘制在系统视频控件下方,但可以响应这些控件未捕获的任何事件。
// 例如,这会将文本“水印”放置在视频区域的最顶部:
VideoPlayer(player: AVPlayer(url: URL(string: "<https://bit.ly/swswift>")!)) {
VStack {
Text("Watermark")
.foregroundStyle(.black)
.background(.white.opacity(0.7))
Spacer()
}
.frame(width: 400, height: 300)
}
在此示例中,您将利用 SwiftUI 的 VideoPlayer
视图,该视图简化了从远程 URL 流式传输视频的过程。 VideoPlayer
视图环绕 AVPlayerViewController
,提供带有播放控件的成熟视频播放器界面。
import AVKit
struct ContentView: View {
var body: some View {
NavigationStack {
VideoPlayer(player: AVPlayer(url: URL(string: "<https://archive.org/download/four_days_of_gemini_4/four_days_of_gemini_4_512kb.mp4>")!))
.navigationTitle("Video Player")
}
}
}
在 SwiftUI 中,为音频和视频创建动画可视化是一件轻而易举的事。您所需要的只是对 Path
和 Shape
协议有基本的了解。
// 创建了一个名为 AnimatedVisualizer 的自定义形状。
// 该形状采用表示音频样本的 CGFloat 值数组。然后使用 path 函数根据提供的音频样本创建可视化效果。
struct AnimatedVisualizer: Shape {
let audioSamples: [CGFloat]
func path(in rect: CGRect) -> Path {
var path = Path()
let height = rect.height
let width = rect.width / CGFloat(audioSamples.count)
for i in 0 ..< audioSamples.count {
let x = width * CGFloat(i)
let y = CGFloat(audioSamples[i]) * height
path.addRect(CGRect(x: x, y: 0, width: width, height: y))
}
return path
}
}
// 然后,在 ContentView 中使用此自定义形状,其中使用 ZStack 将可视化效果添加为背景元素。
// 您还可以添加一些动画,为可视化效果提供炫酷的脉动效果。
struct ContentView: View {
@State private var audioSamples: [CGFloat] = [0.2, 0.5, 0.8, 0.3, 0.6, 0.9, 0.4, 0.4, 0.4, 0.4]
var body: some View {
ZStack {
AnimatedVisualizer(audioSamples: audioSamples)
.fill(Color.red)
.opacity(0.8)
.animation(Animation.easeInOut(duration: 0.2).repeatForever(autoreverses: true), value: audioSamples)
.frame(maxWidth: .infinity, maxHeight: .infinity)
}
// 在 onAppear 块中,您每 0.2 秒生成随机音频样本以动态更新可视化。
.onAppear {
Timer.scheduledTimer(withTimeInterval: 0.2, repeats: true) { timer in
self.audioSamples = self.generateAudioSamples()
}
}
}
func generateAudioSamples() -> [CGFloat] {
var samples: [CGFloat] = []
for _ in 0...10 {
samples.append(CGFloat.random(in: 0...1))
}
return samples
}
}
音频和视频播放错误可能源于一系列问题,包括网络连接问题、文件丢失或放错位置以及不受支持的文件格式等。妥善管理这些错误至关重要,每当出现问题时为用户提供清晰且有用的反馈。
要在本地进行测试,请尝试在禁用网络连接的情况下播放视频。然后您应该会看到显示的错误消息。
import AVKit
struct ContentView: View {
let player = AVPlayer(url: URL(string: "<https://archive.org/download/lunchroom_manners/lunchroom_manners_512kb.mp4>")!)
let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
@State private var isPlaybackLikelyToKeepUp = true
var body: some View {
VStack {
VideoPlayer(player: player)
if !isPlaybackLikelyToKeepUp {
Text("Playback Error: Network load is likely to prevent playback from keeping up.")
}
}
.onReceive(timer, perform: { _ in
isPlaybackLikelyToKeepUp = player.currentItem?.isPlaybackLikelyToKeepUp ?? true
})
}
}
AVKit 框架中的 VideoPlayer
视图用于播放视频。 AVPlayerItem
的 isPlaybackLikelyToKeepUp
属性用于确定网络负载是否妨碍视频播放。如果它可能妨碍播放,则会显示一条用户友好的错误消息。
此方法是处理播放错误的简化方法,特别是对于涉及网络问题的场景。根据您的具体需求,可能需要其他操作来处理各种错误类型。