Get System Volume iOS

Get System Volume iOS

Update for Swift

let vol = AVAudioSession.sharedInstance().outputVolume

The audio session can provide output volume (iOS >= 6.0).

float vol = [[AVAudioSession sharedInstance] outputVolume];
NSLog(@"output volume: %1.2f dB", 20.f*log10f(vol+FLT_MIN));

How to get a current volume of iPhone and change it while playing music using swift?

To get the volume then you will need to use AVAudioSession.sharedInstance().outputVolume as you already have used.

In order to allow the user to control the volume you will want to have a look at MPVolumeView found in the Media Player framework. This component is able to make changes to the system volume.

It is very simple to use:

let volumeView = MPVolumeView(frame: volumeViewSize)
playerView.addSubview(volumeView)

You will want to use this class instead of using your UISlider instance. There are methods supplied that let you override the look and feel of the slider.

Additionally this class exposes a control which allows the user to choose the output route (iPhone, Airpods, Homepod, or Apple TV for example). You can choose to disable either the slider or the output route control so this gives you a fairly broad set of options with customising your user interface.

If you are working with a storyboard then you will need to add it in code. First create an empty UIView in the view controller's view on the storyboard and attach that to your view controller:

A view controller story board with a placeholder view

Once you've added that you will want something like the following in your view controller:

@IBOutlet var volumeSliderContainer: UIView!

private lazy var volumeView: MPVolumeView = {
let volumeView = MPVolumeView(frame: volumeSliderContainer.bounds)
volumeView.showsVolumeSlider = true
volumeView.showsRouteButton = true
volumeView.translatesAutoresizingMaskIntoConstraints = false
return volumeView
}()

override func viewDidLoad() {
super.viewDidLoad()

volumeSliderContainer.addSubview(volumeView)
NSLayoutConstraint.activate([
volumeView.widthAnchor.constraint(equalTo: volumeSliderContainer.widthAnchor),
volumeView.heightAnchor.constraint(equalTo: volumeSliderContainer.heightAnchor),
volumeView.centerXAnchor.constraint(equalTo: volumeSliderContainer.centerXAnchor),
volumeView.centerYAnchor.constraint(equalTo: volumeSliderContainer.centerYAnchor),
])
}

Now you will be able to interact with the MPVolumeView as you require.

Just watch out on the simulator, the component will not render the slider and you will only be able to see it when running your app on an actual device.

How to get System Volume iOS?

You need to initialize the audio session with:

AudioSessionInitialize (NULL, NULL, NULL, NULL);

in viewDidLoad or similar.

However, it's generally a better idea to make use of the new AVAudioSession class if you're targeting iOS 6+, which handles that for you:

float volume = [AVAudioSession sharedInstance].outputVolume;

get current system volume level on iPhone

musicPlayer = [[MPMusicPlayerController iPodMusicPlayer];

currentVolume = musicPlayer.volume;

This is now deprecated as of iOS8.0 so try the following

#import 

AVAudioSession *audioSession = [AVAudioSession sharedInstance];
CGFloat volume = audioSession.outputVolume;

System Volume Change Observer not working on iOS 15

What you're doing is unsupported, so it's not really surprising if it doesn't work on all systems. The correct documented approach is to use KVO on the audio session outputVolume property: https://developer.apple.com/documentation/avfaudio/avaudiosession/1616533-outputvolume

Observing system volume in SwiftUI

Solved. Created a class that conforms to ObservableObject and use the ObservedObject property in the view. Also, the volume observer doesn't work in the simulator, only on device.

VolumeObserver.swift

import Foundation
import MediaPlayer

final class VolumeObserver: ObservableObject {

@Published var volume: Float = AVAudioSession.sharedInstance().outputVolume

// Audio session object
private let session = AVAudioSession.sharedInstance()

// Observer
private var progressObserver: NSKeyValueObservation!

func subscribe() {
do {
try AVAudioSession.sharedInstance().setCategory(AVAudioSession.Category.ambient)
try session.setActive(true, options: .notifyOthersOnDeactivation)
} catch {
print("cannot activate session")
}

progressObserver = session.observe(\.outputVolume) { [self] (session, value) in
DispatchQueue.main.async {
self.volume = session.outputVolume
}
}
}

func unsubscribe() {
self.progressObserver.invalidate()
}

init() {
subscribe()
}
}

ContentView.swift

import SwiftUI
import MediaPlayer

struct ContentView: View {

@ObservedObject private var volObserver = VolumeObserver()

init() {
print(vol.volume)
}

var body: some View {
Text(String(vol.volume))
}
}

struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}

How to set app volume separately from system volume ( iOS device volume physical keys )?

Here's what I did:

  1. Get the current system volume.
  2. Hide volume adjust popup view.
  3. Add observer for changes in system volume.
  4. Set the system volume back to the volume you got(at step 1) in every callbacks you received from the observer.

I'll explain every step in detail.

Step 1 - Get the current system volume

Code for initializing the volumes:

- (void)initializeSystemVolume
{
_originalSystemVolume = [[AVAudioSession sharedInstance] outputVolume];
_currentSystemVolume = _originalSystemVolume;

if(_currentSystemVolume == 0.0)
{
_currentSystemVolume = 0.0625;
}

else if(_currentSystemVolume == 1.0)
{
_currentSystemVolume = 0.9375;
}

[self setSystemVolume:_currentSystemVolume];
}

_originalSystemVolume - This is the volume of the system when entering the app.

_currentSystemVolume - This could also be the same as the original volume BUT this could be changed, while originalSystemVolume should stay the same.

As you can see from the if else statement, I will check first if the current system volume is at the maximum value(1.0) or the minimum value(0.0). Why would I do this?

Because from my experiments, I noticed that the callback of the volume key press would only be made only if the system volume has changed. So if the current system volume is at its minimum value(0.0), and you still pressed the volume - button. No callbacks would be made. Then you would never determine the volume - key press state in this case.

So that is why I need to change the current system volume to a higher volume(0.0625) if it is at its minimum or change it to a lower volume(0.9375) if it's at its maximum so that we will still be able to get callbacks from the system. Now, why 0.0625 and 0.9375?

Well, actually I just want to set it to the closest possible value.
If you would notice, the volume of iOS breaks down into 16 levels, and each level increments by 0.0625. 0.0 is silent mode and 1.0 is the peak volume.

Step 2 - Hide volume adjust popup view

Code for hiding the volume popup:

- (void)moveVolumeChangeNotifSliderOffTheScreen
{
CGRect frame = CGRectMake(0, -100, 10, 0);
MPVolumeView *volumeView = [[MPVolumeView alloc] initWithFrame:frame];
[volumeView sizeToFit];
[[[[UIApplication sharedApplication] windows] objectAtIndex:0] addSubview:volumeView];
}

Since we will not be affecting the system volume, then we shouldn't display the popup as well.

Credit of this code goes to another person. I am sorry I forgot where I got this, but I didn't write it.

Step 3 - Add observer for changes in system volume.

Now, we should listen for changes in the system volume with every key press, and then we can use the value returned by the callback to determine which volume key is pressed.

Code for setting the observer:

- (void)setVolumeChangeObserver
{
[self removeVolumeChangeObserver];

[[AVAudioSession sharedInstance] setActive:YES error:nil];
[[AVAudioSession sharedInstance] addObserver:self forKeyPath:@"outputVolume" options:0 context:nil];
}

Code for removing the observer:

- (void)removeVolumeChangeObserver
{
@try
{
[[AVAudioSession sharedInstance] removeObserver:self forKeyPath:@"outputVolume"];
}

@catch(id anException)
{

}
}

Code for the callback:

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if ([keyPath isEqual:@"outputVolume"])
{
if([[AVAudioSession sharedInstance] outputVolume] < _currentSystemVolume)
{
NSLog(@"Volume key down");

//your code when volume key down is pressed.
}

else if([[AVAudioSession sharedInstance] outputVolume] > _currentSystemVolume)
{
NSLog(@"Volume key up");

//your code when volume key up is pressed.
}

[self removeVolumeChangeObserver];
[self setSystemVolume:_currentSystemVolume];
[self setVolumeChangeObserver];
}
}

As can be seen from the code, we user the outputVolume upon key press and compare it with our _currentSystemVolume that we set awhile ago, we could determine whether volume + is pressed or volume - is pressed.

After analyzing which key is pressed, we should immediately set the system volume back to what it was so that volume key press made within our app will not affect the system volume.

Important: You have to remove the observer first before you set the system volume. Why is that so? Because if you don't do that, once you set the system volume, this callback will be made again, and when that happens the setSystemVolume will be called once more, then callback will be made again, then your setSystemVolume will be called again, then over and over again... You will then create a deadlock on this. By removing the observer, no callbacks will be made.

Step 4 - Setting the system volume

Now, how do we set the system volume then?

Code for setting system volume:

- (void)setSystemVolume:(CGFloat)volume
{
if(_volumeView == nil)
{
_volumeView = [[SystemVolumeView alloc] init];
}

_volumeView.getVolumeSlider.value = volume;
}

_volumeView is an instance of the class, SystemVolumeView, that I made, which extends MPVolumeView to retrieve the UISlider of MPVolumeView. MPVolumeView is the view that pops up when you adjust the volume of the system(media volume).

Code for SystemVolumeView:

SystemVolumeView.h

#import 

@interface SystemVolumeView : MPVolumeView

- (UISlider *)getVolumeSlider;

@end

SystemVolumeView.m

#import 

#import "SystemVolumeView.h"

@interface SystemVolumeView ()

@property UISlider *systemVolumeSlider;

@end

@implementation SystemVolumeView

- (UISlider *)getVolumeSlider
{
if(_systemVolumeSlider != nil)
{
return _systemVolumeSlider;
}

self.showsRouteButton = false;
self.showsVolumeSlider = false;
self.hidden = true;

for(UIView *subview in self.subviews)
{
if([subview isKindOfClass:[UISlider class]])
{
_systemVolumeSlider = (UISlider *)subview;
_systemVolumeSlider.continuous = true;

return _systemVolumeSlider;
}
}

return nil;
}

@end

Credit of this code goes to the accepted answer in this link. I just translated it to Objective-C.

As can be seen from the above code, you can set the system volume by calling getVolumeSlider.value = yourDesiredVolume. yourDesiredVolume should only be of range 0 - 1.

Alright, after all of this, you should have an idea on how these works.

Now, you might noticed we didn't use _originalSystemVolume.

Here's what that was for. Imagine if the volume of the system is set to silent initially, then we would set it to a higher value to make everything work right? Now, once the app enters background, we should set the system volume back to what it was. In this case we would do this when app is resigning active.

- (void)applicationWillResignActive:(UIApplication *)application
{
[self restoreSystemVolume];
}

Code for Restoring system volume:

- (void)restoreSystemVolume
{
[self setSystemVolume:_originalSystemVolume];
}

That's all folks. I hope this answer will be of great help to you someday. :)

Thanks to @Joris van Liempd iDeveloper. This link from him helped me a lot on achieving this.



Related Topics



Leave a reply



Submit