AVAudioPlayer代理方法实现自动播放下一曲:仅播放两首的问题排查
核心问题分析与修复
代理未重新绑定导致后续播放无回调
每次调用configure()都会重新初始化AVAudioPlayer,但仅在viewDidLoad中设置过一次代理,后续新创建的player未绑定代理,导致audioPlayerDidFinishPlaying仅在第一首播放完毕后触发,后续歌曲播放完无自动切换逻辑。
修复:在configure()的player初始化后立即设置代理:do { // ... 原有代码 player = try AVAudioPlayer(contentsOf: URL(fileURLWithPath: urlString)!) guard let player = player else{return} player.delegate = self // 添加这行绑定代理 player.volume = 0.5 player.play() } catch { print("error is: \(error.localizedDescription)") }同时删除
viewDidLoad中的player!.delegate = self,避免强制解包风险。本地文件URL创建错误
使用URL(string: urlString)加载本地文件路径会失败,该方法用于网络URL,本地路径需用URL(fileURLWithPath:)。此错误会导致部分歌曲无法初始化player,表现为播放中断。
修复:修改configure中的URL初始化代码:player = try AVAudioPlayer(contentsOf: URL(fileURLWithPath: urlString)!)viewDidLayoutSubviews重复触发初始化
viewDidLayoutSubviews中当holder.subviews.count == 0时调用configure(),而自动切换时会移除所有子视图,导致该方法再次触发configure(),可能造成position异常或重复初始化。
修复:移除viewDidLayoutSubviews中的configure调用:override func viewDidLayoutSubviews() { // 注释或删除原有判断代码 }滑块定时器内存泄漏与逻辑错误
seekSliderChanged中每次拖动滑块都会创建新的定时器,未清理旧定时器,导致内存泄漏且滑块同步混乱。另外let time = duration/duration等于1,无实际意义。
修复:先销毁旧定时器,再创建新的同步定时器:@objc func seekSliderChanged(_ slider: UISlider) { // 先停止旧定时器 timer?.invalidate() guard let player = player else { return } slider.maximumValue = Float(player.duration) player.currentTime = TimeInterval(slider.value) // 创建新定时器同步滑块 timer = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { [weak self] timer in guard let self = self, let player = self.player else { timer.invalidate() return } slider.value = Float(player.currentTime) } }同时在
viewWillDisappear中添加timer?.invalidate(),避免定时器残留。
额外优化建议
- 避免强制解包
player!,统一使用guard let或if let,防止崩溃。 configureUI()每次都会重新创建所有UI控件,可改为复用现有控件,仅更新数据,提升性能。- 在
audioPlayerDidFinishPlaying中添加边界判断,当播放到最后一首时可重置position或停止播放。
内容的提问来源于stack exchange,提问作者A A

