iOS 9:如何在不显示系统条形图弹出的情况下以编程方式更改音量?

时间:2015-10-16 10:41:40

标签: ios objective-c ios9 mpmusicplayercontroller mpvolumeview

我必须在iPad上更改音量并使用以下代码:

[[MPMusicPlayerController applicationMusicPlayer] setVolume:0];

但这会改变音量并在iPad上显示系统音量条。 如何在不显示音量条的情况下更改声音?

我知道,setVolume:已被弃用,并且每个人都说要使用MPVolumeView。如果这是解决问题的唯一方法,那么如何使用MPVolumeView更改音量?我没有在MPVolumeView中看到任何改变声音的方法。
我应该和MPVolumeView一起使用另一个班级吗?

但最好使用MPMusicPlayerController

感谢您的建议!

12 个答案:

答案 0 :(得分:38)

2018年,在iOS 11.4上工作

您需要在延迟一段时间后更改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
    }
  }
}

用法:

MPVolumeView.setVolume(0.5)

Objective-C version

答案 1 :(得分:16)

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

答案 2 :(得分:4)

我不认为有任何方法可以在不闪烁音量控制的情况下改变音量。你应该像这样使用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];

答案 3 :(得分:3)

@udjat在 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
    }
}

答案 4 :(得分:3)

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

用法:

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

答案 5 :(得分:2)

这是Swift的解决方案。它可能是一个阴暗的,所以如果Apple在我发布时批准了这个,我会告诉你。同时,这对我来说很合适:

  1. 在View Controller中定义MPVolumeView和可选的UISlider

    private let volumeView: MPVolumeView = MPVolumeView()
    private var volumeSlider: UISlider?
    
  2. 在故事板中,定义一个隐藏在用户身上的视图(height = 0应该可以做到这一点),然后为它设置一个出口(我们称之为 hiddenView 这里)。如果您不希望在更改音量时显示音量HUD,请执行此步骤(请参阅下面的注释):

    @IBOutlet weak var hiddenView: UIView!
    
  3. 在viewDidLoad()或运行一次的某个init-y中,将实际控制卷的UISlider从步骤(1)中捕获到可选的UISlider中:

    override func viewDidLoad() {
        super.viewDidLoad()
    
        ...
    
        hiddenView.addSubview(volumeView)
        for view in volumeView.subviews {
            if let vs = view as? UISlider {
                volumeSlider = vs
                break
            }
        }
    }
    
  4. 如果要在代码中设置音量,只需将volumeSlider?.value设置为0.0到1.0之间的任意值,例如:增加音量:

    func someFunc() {
        if volumeSlider?.value < 0.99 {
            volumeSlider?.value += 0.01
        } else {
            volumeSlider?.value = 1.0
        }
    }
    
  5. 重要提示: 当您更改代码中的音量或用户单击外部音量按钮时,此解决方案将阻止iPhone的音量HUD出现。如果您确实要显示HUD,请跳过所有隐藏的视图内容,并且根本不要将MPVolumeView添加为子视图。这将导致iOS在音量改变时显示HUD。

答案 6 :(得分:2)

版本:Swift 3&amp; 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
    }
}

答案 7 :(得分:1)

Swift&gt; 2.2,iOS&gt; 8.0,

我没有找到任何我正在寻找的解决方案,但我最终将其作为解决方案:

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

答案 8 :(得分:1)

这是我的音频播放器应用的音量控制:

import UIKit
import MediaPlayer

class UIVolumeSlider: UISlider {
    
    func activate(){
        updatePositionForSystemVolume()
        
        guard let view = superview else { return }
        let volumeView = MPVolumeView(frame: .zero)
        volumeView.alpha = 0.000001
        view.addSubview(volumeView)
        
        try? AVAudioSession.sharedInstance().setActive(true, options: .notifyOthersOnDeactivation)
        AVAudioSession.sharedInstance().addObserver(self, forKeyPath: "outputVolume", options: .new, context: nil)
        
        addTarget(self, action: #selector(valueChanged), for: .valueChanged)
    }
    
    
    func deactivate(){
        AVAudioSession.sharedInstance().removeObserver(self, forKeyPath: "outputVolume")
        removeTarget(self, action: nil, for: .valueChanged)
        superview?.subviews.first(where: {$0 is MPVolumeView})?.removeFromSuperview()
    }
    
    
    func updatePositionForSystemVolume(){
        try? AVAudioSession.sharedInstance().setActive(true, options: .notifyOthersOnDeactivation)
        value = AVAudioSession.sharedInstance().outputVolume
    }
    
    
    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
        if keyPath == "outputVolume", let newVal = change?[.newKey] as? Float {
            setValue(newVal, animated: true)
        }
    }
    
    
    @objc private func valueChanged(){
        guard let superview = superview else {return}
        guard let volumeView = superview.subviews.first(where: {$0 is MPVolumeView}) as? MPVolumeView else { return }
        guard let slider = volumeView.subviews.first(where: { $0 is UISlider }) as? UISlider else { return }
        
        DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.01) {
            slider.value = self.value
        }
    }
}

如何连接:

  1. 在情节提要上的Identity Inspector中设置UIVolumeSlider类:

enter image description here

  1. 将实例连接到类:
@IBOutlet private var volumeSlider:UIVolumeSlider!
  1. viewDidLoad 方法中:
volumeSlider.updatePositionForSystemVolume()
  1. didAppear 将消失中激活和停用它:
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        volumeSlider.activate()
    }
    
    override func viewWillDisappear(_ animated: Bool) {
        volumeSlider.deactivate()
        super.viewWillDisappear(animated)
    }

答案 9 :(得分:0)

您可以将默认UISlider与此代码一起使用:

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

接下来在代码中写一下

systemSlider.value = customSlide.value

答案 10 :(得分:0)

快捷键4:

var player: AVPlayer!
...
player.volume = 0.5 // 50% level

答案 11 :(得分:0)

Swift 5 / iOS 13

这是我发现的访问系统音量级别的最可靠方法。这是基于此处的其他答案,但不浪费能量,也不需要异步等待。

在初始化期间(例如在viewDidLoad中),创建屏幕外的MPVolumeView

var hiddenSystemVolumeSlider: UISlider!

override func viewDidLoad() {
    let volumeView = MPVolumeView(frame: CGRect(x: -CGFloat.greatestFiniteMagnitude, y:0, width:0, height:0))
    view.addSubview(volumeView)
    hiddenSystemVolumeSlider = volumeView.subviews.first(where: { $0 is UISlider }) as? UISlider
}

然后在需要时使用隐藏的滑块获取或设置系统音量:

var systemVolume:Float {
    get {
        return hiddenSystemVolumeSlider.value
    }
    set {
        hiddenSystemVolumeSlider.value = newValue
    }
}