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

时间:2010-09-06 11:58:50

标签: ios iphone audio ios4 volume

我正在编写一个非常简单的应用程序,按下按钮时会发出声音。由于当设备设置为静音时该按钮没有多大意义,我想在设备的音量为零时禁用它。 (然后当音量再次升高时重新启用它。)

我正在寻找一种工作(和AppStore安全)方式来检测当前音量设置并在音量级别更改时获得通知/回调。我不想改变音量设置。

所有这些都在我使用所述按钮的ViewController中实现。我用运行iOS 4.0.1和4.0.2的iPhone 4以及运行4.0.1的iPhone 3G测试了这个。使用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
);

returnvalue0,这意味着注册回调有效。

可悲的是,当我按下设备上的音量按钮,耳机响铃或翻转铃声静音开关时,我的功能audioVolumeChangeListenerCallback都没有收到回叫。

使用完全相同的代码注册kAudioSessionProperty_AudioRouteChange(在WWDC视频,开发人员文档和互联网上的众多网站上用作类似的示例项目)我实际上更改音频路径时(通过插入/拔出耳机或对接设备)获得回叫。

名为Doug的用户打开了一个标题为iPhone volume changed event for volume already 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开发者论坛或观看WWDC 2010视频。

10 个答案:

答案 0 :(得分:66)

你有没有机会为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:方法在每次按下按钮时都会被击中,即使音量没有因此而改变(因为它已经是最大/分钟)。

答案 1 :(得分:47)

此处某些答案使用的AudioSession API自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)
}

答案 2 :(得分:6)

-(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元素才能使其生效。另请注意,当前音量级别与耳机或扬声器相关(意味着两个音量级别不同,这可以使您获得当前正在使用的设备。这不会回答有关接收音量变化时的通知的问题。

答案 3 :(得分:4)

您是否使用AudioSessionSetActive启动音频会话

答案 4 :(得分:2)

斯威夫特的斯威夫特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)
    }
}

答案 5 :(得分:1)

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

类似的东西:

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

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

答案 6 :(得分:1)

使用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")
 }

答案 7 :(得分:1)

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

答案 8 :(得分:1)

Swift 5 / iOS 13

在测试中,我发现与系统卷交互的最可靠方法是使用MPVolumeView作为每个操作的中介。您已经需要在视图层次结构中的某个位置放置该视图,以使系统隐藏音量更改HUD。

在设置过程中(可能在viewDidLoad()内)创建您的MPVolumeView(如果您不想实际使用系统提供的控件,则在屏幕外):

let systemVolumeView = MPVolumeView(frame: CGRect(x: -CGFloat.greatestFiniteMagnitude, y: 0, width: 0, height: 0))
myContainerView.addSubview(systemVolumeView)
self.systemVolumeSlider = systemVolumeView.subviews.first(where:{ $0 is UISlider }) as? UISlider

获取设置音量:

var volumeLevel:Float {
    get {
        return self.systemVolumeSlider.value
    }
    set {
        self.systemVolumeSlider.value = newValue
    }
}

观察音量(包括通过硬件按钮进行更改):

self.systemVolumeSlider.addTarget(self, action: #selector(volumeDidChange), for: .valueChanged)

@objc func volumeDidChange() {
    // Handle volume change
}

答案 9 :(得分:0)

进入设置 - &gt;声音,然后选中“使用按钮更改”#39; 如果它关闭,按下音量按钮时系统音量不会改变。 也许这就是你没有收到通知的原因。