设备锁定时触发 AVSystemController_SystemVolumeDidChangeNotification

Posted

技术标签:

【中文标题】设备锁定时触发 AVSystemController_SystemVolumeDidChangeNotification【英文标题】:AVSystemController_SystemVolumeDidChangeNotification triggers when device locked 【发布时间】:2018-11-08 15:30:21 【问题描述】:

使用 ios 12,我正在观察 AVSystemController_SystemVolumeDidChangeNotification 以检测音量按下以捕获图像:

let volumeView = MPVolumeView(frame: CGRect(x: 0, y: -40, width: 0, height: 0)) // override volume view
view.addSubview(volumeView)

NotificationCenter.default.addObserver(self, selector: #selector(captureImage), name: Notification.Name(rawValue: "AVSystemController_SystemVolumeDidChangeNotification"), object: nil)

但是,我注意到当按下锁定按钮(位于设备右侧)时,通知也会触发,至少在 iPhone XS 和 XS Max 上是这样。

尝试四处搜索,但没有看到任何人提及此问题或对此通知进行过多讨论。其他类似的聆听音量按钮按下的尝试使用 AVAudioSessions / KVO,但我发现每当我使用时,当音量已经达到最大/最小时,观察者不会被调用。这个AVSystemController_SystemVolumeDidChangeNotification 似乎工作得很好,除了这个奇怪的锁定按钮问题。从通知的名称看不出它为什么会响应按下的锁定按钮。

按下锁定按钮时,我在控制台中收到以下消息:

[avas] AVAudioSessionPortImpl.mm:56:ValidateRequiredFields: Unknown selected data source for Port Speaker (type: Speaker) // 出现四次

+[CATransaction synchronize] 在事务中调用 // 出现两次

按下音量按钮时不会出现这些日志。

另外请注意,我不打算提交 App Store,因此我不担心 Apple 是否会因为使用这种可能的私人通知而拒绝此应用。

如果我创建一个AVAudioSession 而不是AVSystemController_SystemVolumeDidChangeNotification 并像这样观察outputVolume

let audioSession = AVAudioSession()
try? audioSession.setActive(true)
audioSession.addObserver(self, forKeyPath: "outputVolume", options: NSKeyValueObservingOptions.new, context: nil)

…那么当设备锁定时它不会被击中,但我仍然看到“AVAudioSessionPortImpl.mm unknown selected data source for Port Speaker”控制台错误。但是,当音量静音时,它不再接收按下。我想我需要做的是手动更改音量,使其不会达到最小值或最大值?

谢谢

【问题讨论】:

【参考方案1】:

手动上弦,保持音量不变并使用音频会话方法。不得不抛出几个黑客。这有点令人费解,所以我对更清洁的替代品持开放态度。不知道 Apple 会对在应用程序中提交此内容有何反应,尽管他们似乎肯定接受使用音量按钮与相机交互的应用程序。

在 UIViewController 子类中:

override func viewDidLoad() 
    super.viewDidLoad()
    // …
    setupVolumeButton()


private let volumeView = MPVolumeView(frame: CGRect(x: 0, y: -100, width: 0, height: 0)) // override volume view

private func setupVolumeButton() 
    view.addSubview(volumeView)

    setVolume(0.5)  // in case app launches with volume at max/min already
        // need to wait until initial volume setting is done 
        // so it doesn't get triggered on launch

        let audioSession = AVAudioSession()
        try? audioSession.setActive(true)
        audioSession.addObserver(self, forKeyPath: "outputVolume", options: NSKeyValueObservingOptions.new, context: nil)
    


private func setVolume(_ volume: Float, completion: (() -> Void)? = nil) 
    let slider = volumeView.subviews.first(where:  $0 is UISlider ) as? UISlider
    DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.1) 
        slider?.value = volume

        DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.01) 
            // needed to wait a bit before completing so the observer doesn't pick up the manualq volume change
            completion?()
        
    


override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) 
    if keyPath == "outputVolume" 
        setVolume(0.5) // keep from reaching max or min volume so button keeps working

        doTheThingThatShouldHappenWhenTheVolumeButtonIsPressed()
    

编辑:我还注意到,当应用程序关闭时音频会话被停用,所以我将音频会话存储在一个属性中,添加了一个观察者来观察应用程序何时再次激活,并在关联的方法中设置了音频会话再次处于活动状态。

【讨论】:

以上是关于设备锁定时触发 AVSystemController_SystemVolumeDidChangeNotification的主要内容,如果未能解决你的问题,请参考以下文章

如果设备被锁定,CADislayLink 不会触发

在Windows XP上系统锁定/解锁时触发事件

如何在纵向锁定设备时获取设备方向

设备锁定时如何继续录制视频?

设备锁定时,AVAudioPlayer 不会播放

在设备被锁定时响应事件