iOS 9:如何以编程方式更改音量而不显示系统声音栏弹出窗口?

iOS 9:如何以编程方式更改音量而不显示系统声音栏弹出窗口?,ios,objective-c,ios9,mpmusicplayercontroller,mpvolumeview,Ios,Objective C,Ios9,Mpmusicplayercontroller,Mpvolumeview,我必须更改iPad上的音量,并使用以下代码: [[MPMusicPlayerController applicationMusicPlayer] setVolume:0]; import MediaPlayer class CusomViewCOntroller: UIViewController // could be IBOutlet var customSlider = UISlider() // in code var system

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

[[MPMusicPlayerController applicationMusicPlayer] setVolume:0];
    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
       }
    }
但这会改变音量,并在iPad上显示系统音量栏如何在不显示音量栏的情况下更改声音?

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

但最好使用
MPMusicLayerController


谢谢你的建议

我认为如果不闪烁音量控制,就没有办法改变音量。您应该像这样使用
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];

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

Swift>2.2,iOS>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
        }
    }
}

在Swift中有一个解决方案。这可能是一个阴暗的问题,所以当我发布时,我会让你知道苹果是否批准了这一点。同时,这对我来说很好:

  • 在视图控制器中定义MPVolumeView和可选的UISlider

    private let volumeView: MPVolumeView = MPVolumeView()
    private var volumeSlider: UISlider?
    
  • 在故事板中,定义一个对用户隐藏的视图(高度=0应该可以),并为它设置一个出口(我们在这里称之为hiddenView)。只有在更改音量时不想显示音量HUD时,此步骤才有效(请参见下面的注释):

  • 在viewDidLoad()或运行一次的init-y中,从步骤(1)将实际控制卷的UISlider捕获到可选的UISlider中:

  • 如果要在代码中设置音量,只需将volumeSlider?值设置为0.0和1.0之间的任意值,例如,用于增加音量:

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

    版本: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 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
        }
    }
    
    用法:

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

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

    [[MPMusicPlayerController applicationMusicPlayer] setVolume:0];
    
        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
    

    2018年,致力于iOS 11.4

    您需要在一个小延迟后更改
    滑块.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
        }
      }
    }
    
    用法:

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

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

    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
            }
        }
    }
    
    如何连接:

  • 在序列图像板上的标识检查器中设置UIVolumeSlider类:
  • 将实例连接到类:
  • 视图加载中方法:
  • 中激活和停用它会出现并且将消失

  • Swift 5/iOS 13

    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
        }
    }
    
    以下是我找到的访问系统卷级别的最可靠的方法。这是基于这里的其他答案,但不浪费能源,也不需要异步等待

    在初始化期间(例如在
    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
        }
    }
    

    您不应该以编程方式更改卷,这就是
    setVolume:
    被弃用的全部要点。你的应用可能会被拒绝。SWIFTUI怎么办?我们怎么做?这有用吗?我试图使用它,但没有多大成功!我添加了一个视图并将其子类化到MPVolumeView,但它似乎不起作用。为我工作!谢谢!这非常有用,我在iOS 11.3上没有问题。我是在iOS 11.4上做的@OttoBoy@Karl-JohnChow我在这里添加了iOS 11.4上的工作解决方案,您应该如何使用它?Hi@JonathanPlackett,您可以这样尝试:
    self.volumeSlider.value=1//获取最大卷
    BTW,self表示MPVolumeView实例。希望得到帮助。这对我在iOS8上有效,但对iOS9不起作用。是否有iOS9解决方案?在我的例子中,它在iOS 11.4中工作,只设置了更长的截止日期(例如,DispatchTime.now()+0.5)@AndreaGorrieri它仍能以0.01延迟工作,而不显示系统声条。看来你还有另外一个问题。不管怎样,把你的评论投给别人也有同样的问题。谢谢@据我所知,每当初始化
    MPVolumeView
    时(从xib、故事板或编程方式),就会出现此警告。目前,它似乎仍然可以很好地处理此警告,并且没有人能够在这种情况下避免警告。@SimonPham我在iOS 12.0上尝试过,它可以正常工作。确保您在真实设备上运行,而不是在模拟器上运行。如果它仍然不起作用,您可以尝试增加延迟时间,正如上面AndreaGorrieri所说的那样。@SimonPham当然,您必须在前台调用它。使用ViewWillDisplay而不是ViewDidDisplay。
    var systemVolume:Float {
        get {
            return hiddenSystemVolumeSlider.value
        }
        set {
            hiddenSystemVolumeSlider.value = newValue
        }
    }