iOS应用从后台返回时无法隐藏Volume HUD的问题求助
解决后台返回后系统音量HUD重新显示的问题
我之前也碰到过一模一样的情况,问题根源在于应用进入后台后,系统会对MPVolumeView的内部控件做回收处理,同时音频会话的观察者也可能被隐式移除,导致之前的设置直接失效。下面是具体的修复方案和代码调整:
核心问题拆解
- 应用退到后台时,系统会释放MPVolumeView的内部滑块控件,你之前缓存的
systemVolumeSlider会变成无效引用 - 音频会话的观察者在后台阶段可能被系统自动移除,导致音量变化监听失效
- 每次进入
viewWillAppear就新建MPVolumeView实例,会破坏视图层级的稳定性,反而容易出问题
修复步骤与代码调整
- 持久化MPVolumeView实例:把MPVolumeView改成类的属性,避免每次创建新实例
- 监听应用前台通知:在应用从后台回到前台时,重新获取音量滑块并重置音频会话观察者
- 确保观察者的正确添加/移除:避免重复添加观察者,同时确保每次回到前台时观察者处于活跃状态
修改后的完整代码如下:
import UIKit import AVFoundation class YourViewController: UIViewController { private let audioSession = AVAudioSession.sharedInstance() private var systemVolumeSlider: UISlider? private var volumeView: MPVolumeView! override func viewDidLoad() { super.viewDidLoad() // 初始化MPVolumeView,设置为隐藏(不需要显示在界面上) volumeView = MPVolumeView(frame: .zero) volumeView.isHidden = true view.addSubview(volumeView) // 监听应用进入前台的通知,用于重置音量相关设置 NotificationCenter.default.addObserver( self, selector: #selector(handleAppWillEnterForeground), name: UIApplication.willEnterForegroundNotification, object: nil ) } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) // 激活音频会话 do { try audioSession.setActive(true) } catch { print("error: can not setActive audio session") } // 先移除旧的观察者,再添加新的,避免重复添加 audioSession.removeObserver(self, forKeyPath: "outputVolume") audioSession.addObserver( self, forKeyPath: "outputVolume", options: [.old, .new], context: nil ) // 重新获取系统音量滑块,确保引用有效 retrieveSystemVolumeSlider() } override func observeValue( forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer? ) { guard keyPath == "outputVolume", let change = change else { super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context) return } // 在这里处理音量变化的逻辑 let oldVolume = change[.oldKey] as? Float ?? 0 let newVolume = change[.newKey] as? Float ?? 0 print("Volume changed from \(oldVolume) to \(newVolume)") } // 专门的方法来获取系统音量滑块 private func retrieveSystemVolumeSlider() { systemVolumeSlider = volumeView.subviews.compactMap { $0 as? UISlider }.first } // 处理应用回到前台的逻辑 @objc private func handleAppWillEnterForeground() { // 重新获取音量滑块 retrieveSystemVolumeSlider() // 重新激活音频会话(防止后台被暂停) do { try audioSession.setActive(true) } catch { print("error: can not reactivate audio session") } // 重置观察者 audioSession.removeObserver(self, forKeyPath: "outputVolume") audioSession.addObserver( self, forKeyPath: "outputVolume", options: [.old, .new], context: nil ) } deinit { // 清理通知和观察者 NotificationCenter.default.removeObserver(self) audioSession.removeObserver(self, forKeyPath: "outputVolume") } }
额外注意事项
- 不需要把MPVolumeView显示在界面上,设置
isHidden = true即可,它只需要存在于视图层级中就能阻止系统音量HUD显示 - 每次添加观察者前先移除,避免因重复添加导致的多次回调问题
- 在
deinit方法中一定要清理通知和观察者,防止内存泄漏
内容的提问来源于stack exchange,提问作者kouchou
相关产品推荐
相关产品推荐

