Подтвердить что ты не робот

Как получить уровень громкости звука, а громкость изменила уведомления на iOS?

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

Я ищу рабочий (и безопасный для 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, документации для разработчиков и на многочисленных сайтах в сетях), я действительно получаю обратный вызов при изменении аудиодорожки ( путем подключения/выключения гарнитуры или стыковки устройства).

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

Еще один способ, которым я попытался, - зарегистрироваться на NSNotificationCenter, как это.

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

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

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

4b9b3361

Ответ 1

Есть ли вероятность, что ваша подпись была неправильной для метода 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
}

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

Ответ 2

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

Ответ 3

-(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

Вы запустили сеанс аудио с AudioSessionSetActive

Ответ 5

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

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

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

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

Ответ 6

Перейдите в настройки- > звуки и установите флажок "Изменить с помощью кнопок". Если это произойдет, громкость системы не изменится при нажатии кнопок регулировки громкости. Возможно, причина, по которой вы не получили уведомление.

Ответ 7

Добавление в Stuart ответа с использованием 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")
 }

Ответ 8

Свифт 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)
    }
}