Detect volume button press

后端 未结 4 1063
无人及你
无人及你 2020-12-01 09:44

Volume button notification function is not being called.

Code:

func listenVolumeButton(){
    // Option #1
    NSNotificationCenter.defaultCenter().a         


        
相关标签:
4条回答
  • 2020-12-01 10:15

    Using the second method, the value of the key path should be "outputVolume". That is the property we are observing. So change the code to,

    var outputVolumeObserve: NSKeyValueObservation?
    let audioSession = AVAudioSession.sharedInstance()
    
    func listenVolumeButton() {
        do {
            try audioSession.setActive(true)
        } catch {}
    
        outputVolumeObserve = audioSession.observe(\.outputVolume) { (audioSession, changes) in
            /// TODOs
        }
    }
    
    0 讨论(0)
  • 2020-12-01 10:22

    The code above won't work in Swift 3, in that case, try this:

    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")
      }
    }
    
    0 讨论(0)
  • 2020-12-01 10:23

    With this code you can listen whenever the user taps the volume hardware button.

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

    And to listen you just need to add the observer:

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

    You can access the volume value by checking the userInfo:

    @objc private func userInteractedWithVolume(_ notification: Notification) {
        guard let volume = notification.userInfo?[VolumeListener.kVolumeKey] as? Float else {
            return
        }
    
        print("volume: \(volume)")
    }
    
    0 讨论(0)
  • 2020-12-01 10:25
    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
      } 
    }
    

    When observing a new value, I set the system volume back to 0.5. This will probably anger users using music simultaneously, therefore I do not recommend my own answer in production.

    0 讨论(0)
提交回复
热议问题