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

[亡魂溺海] 提交于 2019-11-26 17:45:39

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:

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

For 2018, working on iOS 11.4

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

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

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

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];
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)

@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:

  1. Define an MPVolumeView and an optional UISlider in your View Controller

    private let volumeView: MPVolumeView = MPVolumeView()
    private var volumeSlider: UISlider?
    
  2. 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!
    
  3. 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
            }
        }
    }
    
  4. 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
    }
}

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

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

Swift 4:

var player: AVPlayer!
...
player.volume = 0.5 // 50% level
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!