AVSystemController_SystemVolumeDidChangeNotification срабатывает, когда устройство заблокировано

Используя 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, но я обнаружил, что всякий раз, когда я использовал это, наблюдатель не вызывался, когда громкость уже была на макс / мин. Этот AVSystemController_SystemVolumeDidChangeNotification, кажется, работает нормально, за исключением этой странной проблемы с кнопкой блокировки. Не понимаю, как, судя по названию уведомления, почему оно будет реагировать на нажатие кнопки блокировки.

При нажатии кнопки блокировки в консоли появляются следующие сообщения:

[avas] AVAudioSessionPortImpl.mm:56:ValidateRequiredFields: Неизвестный выбранный источник данных для динамика порта (тип: динамик) // появляется четыре раза

+ [CATransaction synchronize] вызывается внутри транзакции // появляется дважды

Эти журналы не появляются при нажатии кнопки громкости.

Также обратите внимание, что я не планирую отправлять 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 для ошибок консоли Port Speaker. Но затем, когда звук отключен, он больше не получает нажатий. Я думаю, что мне нужно сделать, это вручную изменить громкость, чтобы она не достигала минимального или максимального значения?

Спасибо


person shim    schedule 08.11.2018    source источник


Ответы (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()
    }
}

Изменить: также я заметил, что аудиосеанс был деактивирован, когда приложение было закрыто, поэтому я сохранил аудиосеанс в свойстве, добавил наблюдателя, когда приложение снова стало активным, и в связанном методе я установил аудиосеанс как снова активен.

person shim    schedule 08.11.2018