1

Using iOS 12, I am observing AVSystemController_SystemVolumeDidChangeNotification to detect volume presses to capture images:

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)

However, I've noticed that the notification also fires, at least on an iPhone XS and XS Max, when the lock button (on the right side of the device) is pressed.

Tried searching around and haven't seen anyone mentioning this issue or much discussion of this notification. Other similar attempts to listen to the volume buttons presses use AVAudionSessions / KVO, but I found that whenever I used that the observer did not get called when the volume was already at max/min. This AVSystemController_SystemVolumeDidChangeNotification seems to work just fine, except for this strange lock button issue. Don't see how, from the name of the notification, why it would respond to the lock button being pressed.

When the lock button is pressed I get the following messages in the console:

[avas] AVAudioSessionPortImpl.mm:56:ValidateRequiredFields: Unknown selected data source for Port Speaker (type: Speaker) // this appears four times

+[CATransaction synchronize] called within transaction // this appears twice

These logs do not appear when the volume button is pressed.

Note also I don't plan to submit the App Store, so I am not concerned about whether Apple rejects this app on the basis of using this possibly private notification.

If instead of AVSystemController_SystemVolumeDidChangeNotification I create an AVAudioSession and observe outputVolume like so:

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

… then it isn't hit when the device locks, but I'm still seeing the "AVAudioSessionPortImpl.mm unknown selected data source for Port Speaker" console errors. But then when the volume is muted it doesn't receive presses anymore. I guess what I'd need to do is manually change the volume so it doesn't hit the min or max?

Thank you

4

1 回答 1

0

手动结束,保持音量不变并使用音频会话方法。不得不抛出几个黑客。这有点令人费解,所以我对更清洁的替代品持开放态度。不知道 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()
    }
}

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

于 2018-11-08T17:02:49.303 回答