Detect volume button press

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

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

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

Tags:

Ios

Swift

Ios8