AVSystemController_SystemVolumeDidChangeNotification срабатывает, когда устройство заблокировано - PullRequest
0 голосов
/ 08 ноября 2018

Используя 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] вызывается внутри транзакции // , это появляется дважды

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

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

Спасибо

1 Ответ

0 голосов
/ 08 ноября 2018

Завелась вручную, сохраняя постоянную громкость и используя метод аудио сеанса. Пришлось закинуть пару хаков. Это немного запутанно, поэтому я открыт для более чистых альтернатив. Понятия не имею, как 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()
    }
}

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

...