SwiftUI视图层级中使用AVAudioPlayerDelegate时设置@EnvironmentObject值
解决SwiftUI中音频播放完成后修改@EnvironmentObject的问题
思路一:给代理类添加回调闭包
让MyPlayerDelegate支持闭包回调,在视图中创建代理时,将修改settings的逻辑通过闭包传入,音频播放完成时触发闭包即可修改环境变量。
修改后的完整代码:
import SwiftUI import AVFoundation import MediaPlayer import AVKit struct MeditationView: View { @EnvironmentObject var settings: Settings // 用@StateObject管理代理生命周期,确保和视图同步 @StateObject private var myPlayerDelegate = MyPlayerDelegate() var body: some View { ZStack { settings.rainbowColour .animation(.easeInOut(duration: 1)) VStack { meditationImage(colour: settings.rainbowColour) .resizable() .opacity(settings.showRainbow ? 0 : 1) .scaledToFit() .onAppear() { // 提前设置播放完成后的回调逻辑 myPlayerDelegate.onPlaybackFinished = { [weak self] in guard let self = self else { return } self.settings.showRainbow = true } if let audioFileURL = Bundle.main.url(forResource: settings.meditation, withExtension: "mp3") { do { let audioPlayer = try AVAudioPlayer(contentsOf: audioFileURL) audioPlayer.prepareToPlay() audioPlayer.delegate = myPlayerDelegate audioPlayer.play() // 将播放器存到代理中,方便后续暂停操作 myPlayerDelegate.player = audioPlayer } catch let error { print("无法播放冥想音频\(settings.meditation)") print(error.localizedDescription) } } else { print("无法加载音频文件: \(settings.meditation)") } } .onTapGesture { myPlayerDelegate.player?.pause() settings.showRainbow = true } Text("点击返回彩虹界面") } } } class MyPlayerDelegate : NSObject, AVAudioPlayerDelegate { // 弱引用持有播放器,避免内存泄漏 weak var player: AVAudioPlayer? // 定义播放完成的回调闭包 var onPlaybackFinished: (() -> Void)? func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) { guard flag else { return } // 触发回调 onPlaybackFinished?() } } // 示例冥想图片视图 func meditationImage(colour: Color) -> some View { Image(systemName: "headphones") .foregroundColor(colour) } } // 示例Settings环境对象类 class Settings: ObservableObject { @Published var meditation: String = "sample" @Published var rainbowColour: Color = .blue @Published var showRainbow: Bool = false }
思路二:让代理类持有Settings的弱引用
给MyPlayerDelegate添加弱引用的settings属性,在视图初始化代理时传入环境对象,播放完成后直接通过该引用修改值。注意用weak避免循环引用。
核心代码修改:
class MyPlayerDelegate : NSObject, AVAudioPlayerDelegate { weak var settings: Settings? weak var player: AVAudioPlayer? func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) { guard flag else { return } settings?.showRainbow = true } } // 视图中初始化并传入settings struct MeditationView: View { @EnvironmentObject var settings: Settings @StateObject private var myPlayerDelegate: MyPlayerDelegate init() { let delegate = MyPlayerDelegate() _myPlayerDelegate = StateObject(wrappedValue: delegate) } var body: some View { ZStack { // 原有视图内容... } .onAppear { // 传入环境对象 myPlayerDelegate.settings = settings // 初始化播放器逻辑... } } }
额外优化建议
- 移除全局
audioPlayer变量,改用代理或视图的@State属性持有,避免多视图共享播放器导致的异常。 - 用
@StateObject管理代理生命周期,确保代理不会被提前释放,保证回调能正常触发。 - 播放完成时判断
flag参数,仅在播放成功时执行修改操作。
内容的提问来源于stack exchange,提问作者Andromeda
相关产品推荐
相关产品推荐

