How to get audio volume level, and volume changed notifications on iOS?

Solution 1:

Any chance you did your signature wrong for the volumeChanged: method? This worked for me, dumped in my 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: method gets hit every time the button is pressed, even if the volume does not change as a result (because it's already at max/min).

Solution 2:

The AudioSession API used by some answers here has been deprecated as of iOS 7. It was replaced by AVAudioSession, which exposes an outputVolume property for the system wide output volume. This can be observed using KVO to receive notifications when the volume changes, as pointed out in the documentation:

A value in the range 0.0 to 1.0, with 0.0 representing the minimum volume and 1.0 representing the maximum volume.

The system wide output volume can be set directly only by the user; to provide volume control in your app, use the MPVolumeView class.

You can observe changes to the value of this property by using key-value observing.

You need to ensure your app's audio session is active for this to work:

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

So if all you need is to query the current system volume:

let volume = audioSession.outputVolume

Or we can be notified of changes like so:

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

Don't forget to stop observing before being deallocated:

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

Solution 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;
}

That should get you the current volume level. 1 is max volume, 0 is no volume. Note: no UI elements need to be displayed for this to work. Also note current volume level is relative to headphones or speakers (meaning, the two volume levels are different, and this gets you whichever the device is currently using. This doesn't answer your question regarding receiving notifications of when volume changes.

Solution 4:

did you start the audio session with AudioSessionSetActive