Detect iPhone Volume Button Up Press?

这一生的挚爱 提交于 2019-11-27 12:53:57

There is no documented way to to this, but you can use this workaround. Register for AVSystemController_SystemVolumeDidChangeNotification notification and add an MPVolumeView which will prevent the system volume view from showing up.

MPVolumeView *volumeView = [[MPVolumeView alloc] initWithFrame:CGRectMake(-100, 0, 10, 0)];
[volumeView sizeToFit];
[self.view addSubview:volumeView];

And don't forget to start an Audio Session

AudioSessionInitialize(NULL, NULL, NULL, NULL);
AudioSessionSetActive(true);

In This case, the MPVolumeView is hidden from the user.

As for checking if volume up or down was pressed, just grab the current application's volume

float volumeLevel = [[MPMusicPlayerController applicationMusicPlayer] volume];  

and compare it with new volume after the button was pressed in notification callback

If you don't want to do it by yourself, there's a drop-in class available in github

https://github.com/blladnar/RBVolumeButtons

If you want an event you can register a listener on the "outputVolume" property:

- (void)viewWillAppear:(BOOL)animated {

    AVAudioSession* audioSession = [AVAudioSession sharedInstance];

    [audioSession setActive:YES error:nil];
    [audioSession addObserver:self
                    forKeyPath:@"outputVolume"
                       options:0
                       context:nil];
}

-(void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {

    if ([keyPath isEqual:@"outputVolume"]) {
        NSLog(@"volume changed!");
    }
}

I solved this problem by adding own target/action for UISlider placed inside MPVolumeView. So it's possible to catch volume change events and determine what button was pressed. Here's github repo with implementation of this approach. It works fine with iOS 7 and above, no deprecation warnings and no rejection from Apple.

In order to distinguish volume action: INSTEAD OF (in observeValue guard)

temp != 0.5

USE for only volume up

temp > 0.5

and only detect volume down:

temp < 0.5 

This solution below will print if either volume up or down are pressed.

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
  } 
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!