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:
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:
- Get the current system volume.
- Hide volume adjust popup view.
- Add observer for changes in system volume.
- 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
Swift Editor Placeholder in Source File
How to Scroll to the Bottom of a Uitableview on the iPhone Before the View Appears
Swift Equivalent to '[Nsdictionary Initwithobjects: Forkeys:]'
How to Create a Round Cornered Uilabel on the Iphone
Change the Uitableviewcell Height According to Amount of Text
Attempt to Insert Non-Property List Object When Trying to Save a Custom Object in Swift 3
Apns (Apple Push Notification Service) Reliability
iOS Background Audio Not Playing
Uitableview: How to Disable Selection for Some Rows But Not Others
Adding a Uilabel to a Uitoolbar
How to Get Uikeyboard Size with iOS
How to Show a Custom Uimenuitem for a Uitableviewcell
Check If a Uialertview Is Showing
High Quality Scaling of Uiimage
Setneedslayout VS. Setneedsupdateconstraints and Layoutifneeded VS Updateconstraintsifneeded