【问题标题】:Detect volume button press检测音量按钮按下
【发布时间】:2015-04-12 20:02:41
【问题描述】:

没有调用音量键通知功能。

代码:

func listenVolumeButton(){
    // Option #1
    NSNotificationCenter.defaultCenter().addObserver(self, selector: "volumeChanged:", name: "AVSystemController_SystemVolumeDidChangeNotification", object: nil)
    // Option #2
    var audioSession = AVAudioSession()
    audioSession.setActive(true, error: nil)
    audioSession.addObserver(self, forKeyPath: "volumeChanged", options: NSKeyValueObservingOptions.New, context: nil)
}

override func observeValueForKeyPath(keyPath: String, ofObject object: AnyObject, change: [NSObject : AnyObject], context: UnsafeMutablePointer<Void>) {
    if keyPath == "volumeChanged"{
        print("got in here")
    }
}

func volumeChanged(notification: NSNotification){
   print("got in here")
}

listenVolumeButton() 在 vi​​ewWillAppear 中被调用

无论哪种情况,代码都没有到达打印语句"got in here"

我正在尝试两种不同的方法来做到这一点,但这两种方法都行不通。

我关注了这个:Detect iPhone Volume Button Up Press?

【问题讨论】:

    标签: ios swift ios8


    【解决方案1】:

    使用第二种方法,键路径的值应该是"outputVolume"。这就是我们正在观察的属性。 所以把代码改成,

    var outputVolumeObserve: NSKeyValueObservation?
    let audioSession = AVAudioSession.sharedInstance()
    
    func listenVolumeButton() {
        do {
            try audioSession.setActive(true)
        } catch {}
    
        outputVolumeObserve = audioSession.observe(\.outputVolume) { (audioSession, changes) in
            /// TODOs
        }
    }
    

    【讨论】:

    • 干杯,感谢您的帮助!
    • 当音量最大时,还能收到通知吗?
    • AustinT 你找到绕过最大音量限制的方法了吗?我也有同样的问题。
    • 有人在 swift 3 的 github 上有一些代码示例吗?
    • 嘿@rakeshbs 这个观察者不适用于 ios 11.4 操作系统!是否有任何更新。请让我知道。如果您遇到 ios 11.4 的问题
    【解决方案2】:

    上面的代码在 Swift 3 中不起作用,在这种情况下,试试这个:

    func listenVolumeButton() {
       do {
        try audioSession.setActive(true)
       } catch {
        print("some error")
       }
       audioSession.addObserver(self, forKeyPath: "outputVolume", options: NSKeyValueObservingOptions.new, context: nil)
    }
    
    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
      if keyPath == "outputVolume" {
        print("got in here")
      }
    }
    

    【讨论】:

    • 这对我不起作用——至少在模拟器中不起作用。
    • 不能在模拟器中工作,也不能在已经达到最大音量时触发。
    • 谢谢 - 确认在 iOS 12 / xcode 10 / iPhone X 中工作,尽管您需要在下面添加这一行(我将它作为视图控制器上的类变量): var audioSession = AVAudioSession()
    • 确认在 iOS 12 / xcode 10.2.1 / iPhone SE 下工作,无需将 audiosession 作为类属性。
    【解决方案3】:

    使用此代码,您可以在用户点击音量硬件按钮时收听。

    class VolumeListener {
        static let kVolumeKey = "volume"
    
        static let shared = VolumeListener()
    
        private let kAudioVolumeChangeReasonNotificationParameter = "AVSystemController_AudioVolumeChangeReasonNotificationParameter"
        private let kAudioVolumeNotificationParameter = "AVSystemController_AudioVolumeNotificationParameter"
        private let kExplicitVolumeChange = "ExplicitVolumeChange"
        private let kSystemVolumeDidChangeNotificationName = NSNotification.Name(rawValue: "AVSystemController_SystemVolumeDidChangeNotification")
    
        private var hasSetup = false
    
        func start() {
            guard !self.hasSetup else {
                return
            }
    
            self.setup()
            self.hasSetup = true
    
        }
    
        private func setup() {
            guard let rootViewController = UIApplication.shared.windows.first?.rootViewController else {
                return
            }
    
            let volumeView = MPVolumeView(frame: CGRect.zero)
            volumeView.clipsToBounds = true
            rootViewController.view.addSubview(volumeView)
    
            NotificationCenter.default.addObserver(
                self,
                selector: #selector(self.volumeChanged),
                name: kSystemVolumeDidChangeNotificationName,
                object: nil
            )
    
            volumeView.removeFromSuperview()
        }
    
        @objc func volumeChanged(_ notification: NSNotification) {
            guard let userInfo = notification.userInfo,
                let volume = userInfo[kAudioVolumeNotificationParameter] as? Float,
                let changeReason = userInfo[kAudioVolumeChangeReasonNotificationParameter] as? String,
                changeReason == kExplicitVolumeChange
                else {
                    return
            }
    
            NotificationCenter.default.post(name: "volumeListenerUserDidInteractWithVolume", object: nil,
                                            userInfo: [VolumeListener.kVolumeKey: volume])
        }
    }
    

    要听你只需要添加观察者:

    NotificationCenter.default.addObserver(self, selector: #selector(self.userInteractedWithVolume),
                                               name: "volumeListenerUserDidInteractWithVolume", object: nil)
    

    您可以通过检查用户信息来访问音量值:

    @objc private func userInteractedWithVolume(_ notification: Notification) {
        guard let volume = notification.userInfo?[VolumeListener.kVolumeKey] as? Float else {
            return
        }
    
        print("volume: \(volume)")
    }
    

    【讨论】:

    • 在 iOS 14 上它无需创建 MPVolumeView 即可工作,但不确定早期操作系统是否是这种情况
    • @BenSullivan 在我的 iOS 14.6 iPad 上仍然需要创建 MPVolumeView...
    • 谁能确认这是否会在 iOS 15 上中断?没有为我调用 volumeChanged 选择器。
    • 在 iOS 15 上对其进行了测试,它似乎无法正常工作。有谁知道检测音量变化原因的不同方法?
    【解决方案4】:
    import AVFoundation
    import MediaPlayer
    
    override func viewDidLoad() {
      super.viewDidLoad()
      let volumeView = MPVolumeView(frame: CGRect.zero)
      for subview in volumeView.subviews {
        if let button = subview as? UIButton {
          button.setImage(nil, for: .normal)
          button.isEnabled = false
          button.sizeToFit()
        }
      }
      UIApplication.shared.windows.first?.addSubview(volumeView)
      UIApplication.shared.windows.first?.sendSubview(toBack: volumeView)
    }
    
    override func viewWillAppear(_ animated: Bool) {
      super.viewWillAppear(animated)
      AVAudioSession.sharedInstance().addObserver(self, forKeyPath: "outputVolume", options: NSKeyValueObservingOptions.new, context: nil)
      do { try AVAudioSession.sharedInstance().setActive(true) }
      catch { debugPrint("\(error)") }   
    }
    
    override func viewDidDisappear(_ animated: Bool) {
      super.viewDidDisappear(animated)
      AVAudioSession.sharedInstance().removeObserver(self, forKeyPath: "outputVolume")
      do { try AVAudioSession.sharedInstance().setActive(false) } 
      catch { debugPrint("\(error)") }
    }
    
    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
      guard let key = keyPath else { return }
      switch key {
        case "outputVolume":
          guard let dict = change, let temp = dict[NSKeyValueChangeKey.newKey] as? Float, temp != 0.5 else { return }
          let systemSlider = MPVolumeView().subviews.first { (aView) -> Bool in
            return NSStringFromClass(aView.classForCoder) == "MPVolumeSlider" ? true : false
         } as? UISlider
          systemSlider?.setValue(0.5, animated: false)
          guard systemSlider != nil else { return }
          debugPrint("Either volume button tapped.")
        default:
          break
      } 
    }
    

    当观察到一个新值时,我将系统音量设置回 0.5。这可能会激怒同时使用音乐的用户,因此我不建议在生产中使用我自己的答案。

    【讨论】:

      【解决方案5】:

      如果有兴趣,这里是 RxSwift 版本。

      func volumeRx() -> Observable<Void> {
          Observable<Void>.create {
              subscriber in
              
              let audioSession = AVAudioSession.sharedInstance()
              do {
                  try audioSession.setActive(true)
              } catch let e {
                  subscriber.onError(e)
              }
      
              let outputVolumeObserve = audioSession.observe(\.outputVolume) {
                  (audioSession, changes) in
                  subscriber.onNext(Void())
              }
              
              return Disposables.create {
                  outputVolumeObserve.invalidate()
              }
          }
      }
      

      用法

      volumeRx()
         .subscribe(onNext: {
            print("Volume changed")
         }).disposed(by: disposeBag)
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-03-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-02-10
        • 1970-01-01
        • 2011-11-23
        相关资源
        最近更新 更多