设备锁定时触发AVSystemController_SystemVolumeDidChangeNotification触发

时间:2018-11-08 15:30:22

标签: ios notificationcenter mpvolumeview avsystemcontroller

使用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上也会触发该通知。

尝试搜索,没有人提及此问题或对此通知进行过多讨论。其他尝试收听音量按钮的类似尝试使用AVAudionSessions / KVO,但是我发现,只要我使用过,当音量已经达到max / min时,观察者就不会被调用。这个AVSystemController_SystemVolumeDidChangeNotification似乎工作正常,除了这个奇怪的锁定按钮问题。从通知的名称中看不出为什么它会响应按下的锁定按钮。

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

  

[avas] AVAudioSessionPortImpl.mm:56:ValidateRequiredFields:未知的端口扬声器数据源(类型:扬声器)// 此数据出现四次

     

+ [CATransaction同步]在事务中// // 出现两次

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

请注意,我不打算提交App Store,因此我不担心Apple是否基于使用此可能的私人通知而拒绝了该应用程序。

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

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

…那么当设备锁定时它没有被击中,但是我仍然看到“ AVAudioSessionPortImpl.mm未知的端口扬声器选定数据源”控制台错误。但是,当音量静音时,它不再接收任何按键。我猜我需要做的是手动更改音量,使其不会达到最小值或最大值?

谢谢

1 个答案:

答案 0 :(得分:0)

手动缠绕以保持音量恒定并使用音频会话方法。不得不抛出一些黑客。这有点令人费解,所以我愿意接受更清洁的替代方法。尽管似乎他们肯定接受使用音量按钮与相机进行交互的应用程序,但不知道苹果会对应用程序中提交的内容有何反应。

在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()
    }
}

编辑:另外,我还注意到关闭应用程序时音频会话被停用,因此我将音频会话存储在一个属性中,为该应用程序再次变为活动状态添加了一个观察者,并在关联的方法中设置了音频会话再次激活。