如何在iOS上获得音量级别和音量更改通知?

我正在写一个非常简单的应用程序,按下button时会播放声音。 由于该button没有太大的意义,当设备设置为沉默我想要禁用它,当设备的音量为零。 (随后当音量重新启动时重新启用它。)

我正在寻找工作(和AppStore安全)的方式来检测当前的音量设置,并得到通知/callback时,音量水平的变化。 我不想改变音量设置。

所有这一切都是在我的ViewController中使用上述button的地方实现的。 我已经用运行iOS 4.0.1和4.0.2的iPhone 4以及运行4.0.1的iPhone 3G进行了testing。 使用带有llvm 1.5的iOS SDK 4.0.2构build。 (使用gcc或者llvm-gcc并没有改善任何东西。)在构build实现​​过程中没有任何问题,无论是错误还是警告。 静态分析仪也很高兴。

这是我迄今为止所尝试的,都没有成功。

以下苹果的audio服务文件,我应该注册一个AudioSessionAddPropertyListenerkAudioSessionProperty_CurrentHardwareOutputVolume应该是这样的工作:

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

returnvalue0 ,这意味着注册callback工作。

可悲的是,当我按下我的设备上的音量button,耳机audioVolumeChangeListenerCallback器或翻转振铃器静音开关时,我从来没有得到callback函数audioVolumeChangeListenerCallback

当使用完全相同的代码注册kAudioSessionProperty_AudioRouteChange (在WWDCvideo,开发人员文档和interwebs上的众多站点中用作类似示例项目时),实际上在更改audio路由时(通过插入/退出耳机或对接设备)。

名为Doug的用户打开了一个标题为“ iPhone volume changed event has volume max”的主题 ,他声称自己已经成功使用了这种方法(除非音量实际上不会因为已经设置为最大值而改变)。 不过,这对我不起作用。

我尝试过的另一种方法是像这样在NSNotificationCenter注册。

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

这应该通知我的方法volumeChanged的任何volumeChanged更改,但实际上并没有这样做。

因为共同的信念告诉我,如果一个人努力工作以达到Cocoa的目的,就会做一些根本性的错误,我期望在这里错过一些东西。 很难相信没有简单的方法获得目前的音量水平,但我还没有find一个使用苹果的文档,示例代码,谷歌,苹果开发者论坛或观看WWDC 2010video。

任何你为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 } 

每次按下button时,我的volumeChanged:方法都会被击中,即使音量不会因此而改变(因为它已经是最大/最小值)。

此处某些答案所使用的AudioSession API在iOS 7中已被弃用。它被AVAudioSession替代,该outputVolume为系统范围的输出音量公开一个outputVolume属性。 这可以通过使用KVO在卷更改时接收通知来实现,正如文档中指出的那样:

范围为0.0到1.0的值,其中0.0表示最小音量,1.0表示最大音量。

全系统输出音量只能由用户直接设置; 要在您的应用程序中提供音量控制,请使用MPVolumeView类。

您可以通过使用键值观察来观察对该属性值的更改。

您需要确保您的应用的audio会话处于活动状态,才能正常工作:

 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) } 
 -(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是没有音量。 注意:没有UI元素需要显示这个工作。 另外请注意当前的音量水平是相对于耳机或扬声器(也就是说,这两个音量水平是不同的,这会得到您当前正在使用的设备,这不会回答您接收音量变化通知的问题。

你是否用AudioSessionSetActive开始audio会话?

我认为这取决于其他的实施。 例如,如果使用滑块控制声音的音量,则可以通过UIControlEventValueChanged进行检查操作,如果您获得0值,则可以将该button设置为隐藏或禁用。

就像是:

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

其中void checkZeroVolume可以做实际的音量比较,因为它是任何音量变化后触发。

进入设置 – >声音,并检查“button更改”。 如果closures,按音量button时系统音量不会改变。 也许这就是你没有得到通知的原因。

使用AVAudioSession添加到Stuart的答案来说明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") } 

斯图亚特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) } }