iOS 9: How to change volume programmatically without showing system sound bar popup?


iOS 9: How to change volume programmatically without showing system sound bar popup?



I have to change the volume on iPad and using this code:


[[MPMusicPlayerController applicationMusicPlayer] setVolume:0];



But this changing volume and showing system volume bar on iPad. How to change the sound without showing the volume bar?



I know, setVolume: is deprecated, and everybody says to use MPVolumeView. If this is the only way to solve my problem, then how to change the volume using MPVolumeView? I don't see any method in MPVolumeView that changes the sound.
Should I use some another class together with MPVolumeView?


setVolume:


MPVolumeView


MPVolumeView


MPVolumeView


MPVolumeView



But it's preferable to use MPMusicPlayerController.


MPMusicPlayerController



Thank you for advice!





You're not supposed to change the volume programmatically, that's the whole point of setVolume: being deprecated. You app could be rejected.
– Alejandro Iván
Dec 2 '15 at 17:02


setVolume:




9 Answers
9



MPVolumeView has a slider, and by changing the value of the slider, you can change the device volume. I wrote an MPVolumeView extension to easily access the slider:


MPVolumeView


MPVolumeView


extension MPVolumeView {
var volumeSlider:UISlider {
self.showsRouteButton = false
self.showsVolumeSlider = false
self.hidden = true
var slider = UISlider()
for subview in self.subviews {
if subview.isKindOfClass(UISlider){
slider = subview as! UISlider
slider.continuous = false
(subview as! UISlider).value = AVAudioSession.sharedInstance().outputVolume
return slider
}
}
return slider
}
}





Did this work? I am trying to use it without much success! I added a view and subclassed it to MPVolumeView but it doesn't seem to be working.
– Gugulethu
Jun 8 '16 at 10:43





Work for me! Thx!
– Jerome
Dec 13 '16 at 8:51





this was super useful
– Fluidity
Jul 4 '17 at 5:33





Does this still work in iOS 11.4?
– Karl-John Chow
Jun 7 at 7:53





@Karl-JohnChow I added working solution on iOS 11.4 here stackoverflow.com/questions/33168497/…
– trungduc
Jun 7 at 11:52




For 2018, working on iOS 11.4



You need to change slider.value after a small delay.


slider.value


extension MPVolumeView {
static func setVolume(_ volume: Float) {
let volumeView = MPVolumeView()
let slider = volumeView.subviews.first(where: { $0 is UISlider }) as? UISlider

DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.01) {
slider?.value = volume;
}
}
}



Usage:


MPVolumeView.setVolume(0.5)



Objective-C version





In my case it's working in iOS 11.4 only setting a much longer deadline (e.g., DispatchTime.now() + 0.5)
– Andrea Gorrieri
2 days ago





@AndreaGorrieri It still works with 0.01 delay without showing system sound bar. Seem like maybe you have another additional problem here. Anyway, upvoted your comment for someone else has same issue. Thanks!
– trungduc
2 days ago





This works for me, thanks. But each time the volume is increased, I get the following log: <<<< AVOutputDeviceDiscoverySession (FigRouteDiscoverer) >>>> -[AVFigRouteDiscovererOutputDeviceDiscoverySessionImpl outputDeviceDiscoverySessionDidChangeDiscoveryMode:]: Setting device discovery mode to DiscoveryMode_None Has anyone been able to get rid of that message?
– KerCodex
12 hours ago





@KerCodex As I know, this warning appears whenever MPVolumeView is initialized (from xib, storyboard or programmatically). Currently, seem like it still works good with this warning and no one has solution to avoid warning in this case.
– trungduc
9 hours ago



MPVolumeView



I don't think there is any way to change the volume without flashing volume control. You should use MPVolumeView like this:


MPVolumeView


MPVolumeView* volumeView = [[MPVolumeView alloc] init];

// Get the Volume Slider
UISlider* volumeViewSlider = nil;

for (UIView *view in [volumeView subviews]){
if ([view.class.description isEqualToString:@"MPVolumeSlider"]){
volumeViewSlider = (UISlider*)view;
break;
}
}

// Fake the volume setting
[volumeViewSlider setValue:1.0f animated:YES];
[volumeViewSlider sendActionsForControlEvents:UIControlEventTouchUpInside];





This works for me on iOS8 but does NOT work on iOS9. Is there a iOS9 workaround?
– frakman1
Jul 18 '17 at 4:52



@udjat 's answer in Swift 3


extension MPVolumeView {
var volumeSlider: UISlider? {
showsRouteButton = false
showsVolumeSlider = false
isHidden = true
for subview in subviews where subview is UISlider {
let slider = subview as! UISlider
slider.isContinuous = false
slider.value = AVAudioSession.sharedInstance().outputVolume
return slider
}
return nil
}
}



Here's a solution in Swift. It might be a shady one, so I'll let you know if Apple approved this when I publish. Meantime, this works just fine for me:



Define an MPVolumeView and an optional UISlider in your View Controller


private let volumeView: MPVolumeView = MPVolumeView()
private var volumeSlider: UISlider?



In the storyboard, define a view that's hidden from the user (height=0 should do the trick), and set an outlet for it (we'll call it hiddenView here). This step is only good if you want NOT to display the volume HUD when changing the volume (see note below):


@IBOutlet weak var hiddenView: UIView!



In viewDidLoad() or somewhere init-y that runs once, catch the UISlider that actually controls the volume into the optional UISlider from step (1):


override func viewDidLoad() {
super.viewDidLoad()

...

hiddenView.addSubview(volumeView)
for view in volumeView.subviews {
if let vs = view as? UISlider {
volumeSlider = vs
break
}
}
}



When you want to set the volume in your code, just set volumeSlider?.value to be anywhere between 0.0 and 1.0, e.g. for increasing the volume:


func someFunc() {
if volumeSlider?.value < 0.99 {
volumeSlider?.value += 0.01
} else {
volumeSlider?.value = 1.0
}
}



Important note:
This solution will prevent the iPhone's Volume HUD from appearing - either when you change the volume in your code, or when the user clicks the external volume buttons. If you do want to display the HUD, then skip all the hidden view stuff, and don't add the MPVolumeView as a subview at all. This will cause iOS to display the HUD when the volume changes.



Version: Swift 3 & Xcode 8.1


extension MPVolumeView {
var volumeSlider:UISlider { // hacking for changing volume by programing
var slider = UISlider()
for subview in self.subviews {
if subview is UISlider {
slider = subview as! UISlider
slider.isContinuous = false
(subview as! UISlider).value = AVAudioSession.sharedInstance().outputVolume
return slider
}
}
return slider
}
}





how are you supposed to use this?
– Jonathan Plackett
Jan 3 '17 at 17:39





Hi @JonathanPlackett, you can try like this: self.volumeSlider.value = 1 // get the max volume BTW, self means MPVolumeView instance. Hope for help.
– Jerome
Jan 4 '17 at 0:39



self.volumeSlider.value = 1 // get the max volume



Swift > 2.2, iOS > 8.0,



I didn't find any solution I was looking but I end up doing this as solution:


let volumeView = MPVolumeView()

override func viewDidLoad() {
...
view.addSubview(volumeView)
volumeView.alpha = 0.00001
}

func changeSpeakerSliderPanelControls(volume: Float) {
for subview in self.volumeView.subviews {

if subview.description.rangeOfString("MPVolumeSlider") != nil {
let slider = subview as! UISlider
slider.value = volume

break
}
}
}


extension UIViewController {
func setVolumeStealthily(_ volume: Float) {
guard let view = viewIfLoaded else {
assertionFailure("The view must be loaded to set the volume with no UI")
return
}

let volumeView = MPVolumeView(frame: .zero)

guard let slider = volumeView.subviews.first(where: { $0 is UISlider }) as? UISlider else {
assertionFailure("Unable to find the slider")
return
}

volumeView.clipsToBounds = true
view.addSubview(volumeView)

DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.1) { [weak slider, weak volumeView] in
slider?.setValue(volume, animated: false)
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.1) { [weak volumeView] in
volumeView?.removeFromSuperview()
}
}
}
}



Usage:


// set volume to 50%
viewController.setVolume(0.5)



You can use default UISlider with this code:


import MediaPlayer

class CusomViewCOntroller: UIViewController

// could be IBOutlet
var customSlider = UISlider()

// in code
var systemSlider = UISlider()

override func viewDidLoad() {
super.viewDidLoad()

let volumeView = MPVolumeView()
if let view = volumeView.subviews.first as? UISlider{
systemSlider = view
}
}



next in code just write


systemSlider.value = customSlide.value






By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Comments

Popular posts from this blog

paramiko-expect timeout is happening after executing the command

how to run turtle graphics in Colaboratory

Export result set on Dbeaver to CSV