Как получить уровень громкости звука и уведомления об изменении громкости на iOS? - PullRequest
54 голосов
/ 06 сентября 2010

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

Я ищу рабочий (и безопасный для AppStore) способ определить текущую настройку громкости и получить уведомление / обратный вызов, когдауровень громкости меняется.Я не хочу изменять настройку громкости .

Все это реализовано в моем ViewController, где используется указанная кнопка.Я проверил это на iPhone 4 под управлением iOS 4.0.1 и 4.0.2, а также на iPhone 3G под управлением 4.0.1.Построен с iOS SDK 4.0.2 с llvm 1.5.(Использование gcc или llvm-gcc ничего не улучшает.) При реализации сборки нет проблем, ни ошибок, ни предупреждений.Статический анализатор тоже доволен.

Вот то, что я пробовал до сих пор, но все безуспешно.

Следуя документации Apple по аудио сервисам, я должен зарегистрировать AudioSessionAddPropertyListener для kAudioSessionProperty_CurrentHardwareOutputVolumeкоторый должен работать так:

// Registering for Volume Change notifications
AudioSessionInitialize(NULL, NULL, NULL, NULL);
returnvalue = AudioSessionAddPropertyListener (

kAudioSessionProperty_CurrentHardwareOutputVolume ,
      audioVolumeChangeListenerCallback,
      self
);

returnvalue равен 0, что означает, что регистрация обратного вызова сработала.

К сожалению, я никогда не получаю обратный вызов своей функции audioVolumeChangeListenerCallbackкогда я нажимаю кнопки регулировки громкости на моем устройстве, щелкаю гарнитуру или переключаю переключатель бесшумного звонка.

При использовании точно такого же кода для регистрации на kAudioSessionProperty_AudioRouteChange (который используется в качестве аналогичного примера проекта в WWDCвидео, документация для разработчиков и на многочисленных сайтах в сети Интернет) Я на самом деле делаю получаю обратный вызов при изменении аудио-маршрута (подключая / вынимая гарнитуру или подключая устройство).

Aпользователь с именем Дуг открыл поток с названием Событие изменения громкости iPhone для объема, уже максимального , где он заявил, что успешно использует этот способ (если тольковремя действительно не изменится, потому что оно уже установлено на максимум).Тем не менее, это не работает для меня.

Другой способ, который я пробовал, это зарегистрироваться на NSNotificationCenter, как это.

// sharedAVSystemController 
AudioSessionInitialize(NULL, NULL, NULL, NULL);
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
[notificationCenter addObserver:self
                                         selector:@selector(volumeChanged:) 
                                             name:@"AVSystemController_SystemVolumeDidChangeNotification" 
                                           object:nil];

Это должно уведомить мой метод volumeChanged о любомSystemVolume меняется, но на самом деле это не так.

Так как общее убеждение говорит мне, что если кто-то слишком усердно работает, чтобы достичь чего-то с Какао, он делает что-то в корне неправильно, я ожидаю что-то здесь упустить,Трудно поверить, что нет простого способа до получить текущий уровень громкости, но я не смог найти его, используя документацию Apple, пример кода, Google, Apple DeveloperФорумы или просмотр видео WWDC 2010.

Ответы [ 9 ]

66 голосов
/ 24 июня 2011

Есть ли вероятность, что вы неправильно сделали свою подпись для метода volumeChanged:?Это сработало для меня, выгружено в моем appdelegate:

- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    [[NSNotificationCenter defaultCenter]
     addObserver:self
     selector:@selector(volumeChanged:)
     name:@"AVSystemController_SystemVolumeDidChangeNotification"
     object:nil];
}

- (void)volumeChanged:(NSNotification *)notification
{
    float volume =
    [[[notification userInfo]
      objectForKey:@"AVSystemController_AudioVolumeNotificationParameter"]
     floatValue];

    // Do stuff with volume
}

Мой volumeChanged: метод срабатывает каждый раз, когда нажимается кнопка, даже если в результате громкость не изменяется (потому что она уже на максимуме / мин.).

48 голосов
/ 20 марта 2015

API AudioSession, используемый некоторыми ответами здесь, устарел начиная с iOS 7. Он был заменен на AVAudioSession, который предоставляет свойство outputVolume для общесистемного выходного тома.Это можно наблюдать, используя KVO для получения уведомлений при изменении громкости, как указано в документации:

Значение в диапазоне от 0,0 до 1,0, где 0,0 обозначает минимальный объем, а 1,0 обозначает максимальныйгромкость.

Общесистемная выходная громкость может быть установлена ​​непосредственно только пользователем;чтобы обеспечить регулировку громкости в вашем приложении, используйте класс MPVolumeView.

Вы можете наблюдать изменения значения этого свойства с помощью наблюдения значения ключа.

Вам необходимо убедиться, чтоАудио сеанс приложения активен, чтобы это работало:

let audioSession = AVAudioSession.sharedInstance()
do {
    try audioSession.setActive(true)
    startObservingVolumeChanges()
} catch {
    print(“Failed to activate audio session")
}

Так что, если все, что вам нужно, это запросить текущий объем системы:

let volume = audioSession.outputVolume

Или мы можем получать уведомления об изменениях, например:

private struct Observation {
    static let VolumeKey = "outputVolume"
    static var Context = 0

}

func startObservingVolumeChanges() {
    audioSession.addObserver(self, forKeyPath: Observation.VolumeKey, options: [.Initial, .New], context: &Observation.Context)
}

override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
    if context == &Observation.Context {
        if keyPath == Observation.VolumeKey, let volume = (change?[NSKeyValueChangeNewKey] as? NSNumber)?.floatValue {
            // `volume` contains the new system output volume...
            print("Volume: \(volume)")
        }
    } else {
        super.observeValueForKeyPath(keyPath, ofObject: object, change: change, context: context)
    }
}

Не забудьте прекратить наблюдение, прежде чем его освободят:

func stopObservingVolumeChanges() {
    audioSession.removeObserver(self, forKeyPath: Observation.VolumeKey, context: &Observation.Context)
}
6 голосов
/ 04 февраля 2012
-(float) getVolumeLevel
{
    MPVolumeView *slide = [MPVolumeView new];
    UISlider *volumeViewSlider;

    for (UIView *view in [slide subviews]){
        if ([[[view class] description] isEqualToString:@"MPVolumeSlider"]) {
            volumeViewSlider = (UISlider *) view;
        }
    }

    float val = [volumeViewSlider value];
    [slide release];

    return val;
}

Это даст вам текущий уровень громкости. 1 - максимальная громкость, 0 - нет громкости. Примечание: для этого не нужно отображать элементы пользовательского интерфейса. Также обратите внимание, что текущий уровень громкости относится к наушникам или динамикам (это означает, что два уровня громкости различны, и это позволяет вам узнать, какое устройство в настоящее время используется. Это не отвечает на ваш вопрос относительно получения уведомлений о том, когда изменяется громкость.

4 голосов
/ 06 сентября 2010

вы начали аудио сеанс с AudioSessionSetActive

2 голосов
/ 15 августа 2017

Swift 3 версия отличного ответа Стюарта:

let audioSession = AVAudioSession.sharedInstance()

do {
    try audioSession.setActive(true)
    startObservingVolumeChanges()
} 
catch {
    print("Failed to activate audio session")
}

let volume = audioSession.outputVolume

private struct Observation {
    static let VolumeKey = "outputVolume"
    static var Context = 0
}

func startObservingVolumeChanges() {
    audioSession.addObserver(self, forKeyPath: Observation.VolumeKey, options: [.Initial, .New], context: &Observation.Context)
}

override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
    if context == &Observation.Context {
        if keyPath == Observation.VolumeKey, let volume = (change?[NSKeyValueChangeNewKey] as? NSNumber)?.floatValue {
            // `volume` contains the new system output volume...
            print("Volume: \(volume)")
        }
    } else {
        super.observeValueForKeyPath(keyPath, ofObject: object, change: change, context: context)
    }
}
1 голос
/ 19 января 2018

Свифт 4

func startObservingVolumeChanges() {
    avAudioSession.addObserver(self, forKeyPath: Observation.VolumeKey, options: [.initial, .new], context: &Observation.Context)
}

override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
    if context == &Observation.Context {
        if keyPath == Observation.VolumeKey, let volume = (change?[NSKeyValueChangeKey.newKey] as? NSNumber)?.floatValue {
            print("\(logClassName): Volume: \(volume)")
        }
    } else {
        super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
    }
}

func stopObservingVolumeChanges() {
    avAudioSession.removeObserver(self, forKeyPath: Observation.VolumeKey, context: &Observation.Context)
}

и тогда вы звоните

var avAudioSession = AVAudioSession.sharedInstance()
try? avAudioSession.setActive(true)
startObservingVolumeChanges()
1 голос
/ 26 марта 2017

Добавление ответа Стюарта с использованием AVAudioSession для учета некоторых изменений в Swift 3. Надеюсь, код прояснит, куда направляется каждый компонент.

override func viewWillAppear(_ animated: Bool) {
    listenVolumeButton()
}

func listenVolumeButton(){
   let audioSession = AVAudioSession.sharedInstance()
   do{
       try audioSession.setActive(true)
       let vol = audioSession.outputVolume
       print(vol.description) //gets initial volume
     }
   catch{
       print("Error info: \(error)")
   }
   audioSession.addObserver(self, forKeyPath: "outputVolume", options: 
   NSKeyValueObservingOptions.new, context: nil)
}

override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
    if keyPath == "outputVolume"{
        let volume = (change?[NSKeyValueChangeKey.newKey] as 
        NSNumber)?.floatValue
        print("volume " + volume!.description)
    }
}

 override func viewWillDisappear(_ animated: Bool) {
     audioSession.removeObserver(self, forKeyPath: "outputVolume")
 }
1 голос
/ 06 сентября 2010

Я думаю, что это зависит от другой реализации.Например, если вы используете ползунок для управления громкостью звука, вы можете выполнить действие проверки с помощью UIControlEventValueChanged, а если получите значение 0, вы можете установить кнопку скрытой или отключенной.

Что-то вроде:

[MusicsliderCtl addTarget:self action:@selector(checkZeroVolume:)forControlEvents:UIControlEventValueChanged];

где void checkZeroVolume может сравнивать фактический объем, так как он запускается после любого изменения объема.

0 голосов
/ 17 апреля 2014

Зайдите в настройки-> звуки и отметьте «Изменить кнопками». Если он выключен, громкость системы не изменится при нажатии кнопок громкости. Может быть, это причина, почему вы не получили уведомление.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...