5

Is there a way to specifically detect the button presses, NOT the volume change events. I tried following these two posts, but they detect volume change NOT button presses:

Cleanest way of capturing volume up/down button press on iOS8

swift detect volume button press

The reason I cannot use these two events, is because my app automatically adjusts the volume during certain events. When the app does this, it is not the same event as when the user manually presses the volume buttons.

Community
  • 1
  • 1
Pavel
  • 704
  • 11
  • 25
  • Why do you need to know when the button specifically is pressed? – Olivier Wilkinson Oct 01 '16 at 22:38
  • It's an Alarm app, so when an alarm is triggered, my app changes the system volume from its current level to maximum level. This volume changes are programatic, and will trigger the following even though the user did NOT press the volume buttons: AVAudioSession.sharedInstance().addObserver(self, forKeyPath: "outputVolume", options: NSKeyValueObservingOptions.New, context: nil) – Pavel Oct 01 '16 at 22:54
  • Makes sense, would it be easier to just remove the observer after the alarm goes off? Is that not viable in your code? – Olivier Wilkinson Oct 01 '16 at 22:57
  • I actually want the observer to be active when the alarm goes off (is triggered) because I want the volume buttons to silence the alarm. – Pavel Oct 01 '16 at 22:59
  • Ah I see. I doubt there is a way to detect the button presses directly, I'm going to have a think about alternate options. – Olivier Wilkinson Oct 01 '16 at 23:01
  • I noticed the following during my research, but I feel there may be several issues with it: https://github.com/jpsim/JPSVolumeButtonHandler 1. It may be using some of the same techniques to detect volume button presses, by detecting volume change events. 2. Their code may interfere with my code as we both seem to access and manipulate same shared global instances: AVAudioSession.sharedInstance()... 3. I see it uses MPMusicPlayerController in the setSystemVolume method which I believe is now deprecated. – Pavel Oct 01 '16 at 23:02
  • What problems do you think it could cause? – Olivier Wilkinson Oct 01 '16 at 23:09
  • 1
    1. It may be using some of the same techniques to detect volume button presses, by detecting volume change events. 2. Their code may interfere with my code as we both seem to access and manipulate same shared global instances: AVAudioSession.sharedInstance()... 3. I see it uses MPMusicPlayerController in the setSystemVolume method which I believe is now deprecated. – Pavel Oct 01 '16 at 23:13
  • Very good points. So what I'd do (not ideally but as a fix) is subscribe to the notification like they do in the questions you linked and then add a variable named say programmaticVolumeChange. When you change the volume programmatically set the variable to true, then in your function observeValueForKeyPath, if the variable is true don't cancel the alarm (and naturally set it to false after). That way when the user presses the volume buttons you know it wasn't your code. Maybe test the amount of time between a programmatic volume change and the function call but I think that would be fine. – Olivier Wilkinson Oct 01 '16 at 23:23
  • If not, delve into the code for the GitHub project and check whether it would cause problems or not. – Olivier Wilkinson Oct 01 '16 at 23:29
  • That's a great idea. You should add it to the suggested solutions, as someone later on can benefit from this. – Pavel Oct 01 '16 at 23:37
  • Will do, glad I could help! :) – Olivier Wilkinson Oct 01 '16 at 23:41

1 Answers1

5

So what I'd do is subscribe to the notification like they do in the questions you linked and then add a variable named say programmaticVolumeChange. When you change the volume programmatically set the variable to true, then in your function observeValueForKeyPath, if the variable is true don't cancel the alarm (and naturally set it to false after). That way when the user presses the volume buttons you know it wasn't your code. Maybe test the amount of time between a programmatic volume change and the function call but I think that would be fine.

Eg

var programmaticVolumeChange = false

func changeVolumeProgramatically() {

    programmaticVolumeChange = true
    //change volume programmatically after

}

override func observeValueForKeyPath(keyPath: String, ofObject object: AnyObject,
change: [NSObject : AnyObject], context: UnsafeMutablePointer<Void>) {

    if keyPath == "outputVolume"{

        if programmaticVolumeChange {

            programmaticVolumeChange = false

        } else {

            //handle logic for user volume button press

        }

    }

}
Olivier Wilkinson
  • 2,836
  • 15
  • 17
  • **Swift 5** Doesn't work from the class code + doesn't work from viewDidLoad – J A S K I E R Mar 10 '20 at 07:05
  • Great solution. This works with Swift 5, though I used `NotificationCenter.default.addObserver(self, selector: #selector(volumeChanged(notification:)), name: NSNotification.Name(rawValue: "AVSystemController_SystemVolumeDidChangeNotification"), object: nil)` (see linked answers from OP's question with more details on this) – daspianist May 27 '20 at 18:32