objective-c 程序访问 iPhone 音量按钮

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/772832/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-03 21:25:39  来源:igfitidea点击:

program access to iPhone volume buttons

iphoneobjective-c

提问by COTOHA

Is there any way to subscribe to volume buttons press events?

有没有办法订阅音量按钮按下事件?

采纳答案by lostInTransit

After the recent rejections from Apple

在最近被苹果拒绝之后

Do not use this. Apple now uses some patch which would reject your app straightaway if it uses any of the private APIs - though should note here that quite some apps on the App Store use this already and are still there!

不要使用这个。Apple 现在使用了一些补丁,如果它使用任何私有 API,它会立即拒绝您的应用程序 - 但在这里应该注意,App Store 上的相当多的应用程序已经使用它并且仍然存在!

The only way to do this now is to have an AVAudioPlayer prepared to play but not playing ([player prepareToPlay]). This seems to take care of adjusting the app's volume according to the rocker buttons.

现在唯一的方法是让 AVAudioPlayer 准备播放但不播放([player prepareToPlay])。这似乎负责根据摇杆按钮调整应用程序的音量。

There's no other published way currently to handle this.

目前没有其他已发布的方法来处理这个问题。

PLEASE READ THE ABOVE NOTE

请阅读以上注意事项

Yes, Use the MPVolumeView

是的,使用 MPVolumeView

MPVolumeView *volume = [[[MPVolumeView alloc] initWithFrame:CGRectMake(18.0, 340.0, 284.0, 23.0)] autorelease];
  [[self view] addSubview:volume];

  [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(volumeChanged:) 
                                        name:@"AVSystemController_SystemVolumeDidChangeNotification" 
                                        object:nil];    
  for (UIView *view in [volume subviews]){
    if ([[[view class] description] isEqualToString:@"MPVolumeSlider"]) {
      volumeViewSlider = view;  //volumeViewSlider is a UIView * object
    }
  }
  [volumeViewSlider _updateVolumeFromAVSystemController];

-(IBAction)volumeChanged:(id)sender{
  [volumeViewSlider _updateVolumeFromAVSystemController];
}

This will give you a slider (same as one used in ipod) whose value will change acc to volume of the phone

这将为您提供一个滑块(与 ipod 中使用的滑块相同),其值将根据手机的音量变化

You will get a compile-time warning that view may not respond to _updateVolumeFromAVSystemControl, but just ignore it.

您将收到一个编译时警告,即视图可能不会响应 _updateVolumeFromAVSystemControl,而只是忽略它。

回答by William Denniss

If you just want to get the notifications, I think it is like this:

如果你只是想得到通知,我认为是这样的:

Please correct me if I am wrong, but I don't believe this uses any internal API.

如果我错了,请纠正我,但我不相信这使用了任何内部 API。

[[NSNotificationCenter defaultCenter] addObserver:self
        selector:@selector(volumeChanged:) 
        name:@"AVSystemController_SystemVolumeDidChangeNotification" 
        object:nil];

Details of this event are here: http://www.cocoadev.com/index.pl?AVSystemController

此事件的详细信息在这里:http: //www.cocoadev.com/index.pl?AVSystemController

The other replies here seem to be based on this hack: http://blog.stormyprods.com/2008/09/proper-usage-of-mpvolumeview-class.htmlwhich was a workaround for a now-fixed bug.

此处的其他回复似乎基于此 hack:http: //blog.stormyprods.com/2008/09/proper-usage-of-mpvolumeview-class.html,这是现在修复的错误的解决方法。

But I'm pretty sure if all you want to do is GETthe notification, and not SETthe system volume, you can just use the notification center like with any other event!!

但是我很确定如果您只想获取通知而不是设置系统音量,您可以像处理任何其他事件一样使用通知中心!!

Be advised: since Apple added the volume-up action to the camera, this notification is notposted while a UIImagePickerControlleris visible.

请注意:由于 Apple 向相机添加了音量增大操作,因此在 a可见时不会发布此通知UIImagePickerController

回答by marco

The easiest and most functionally complete way to do this that I have found in studying all the sources mentioned above and in other threads is: JPSVolumeButtonHandler(I am not involved other than being a user. But thanks a lot to the people responsible!)

我在研究上述所有来源和其他线程中发现的最简单且功能最完整的方法是:JPSVolumeButtonHandler(除了作为用户之外,我不参与其中。但非常感谢负责人!)

EDIT: Release 1.0.2 came with some significant changes/enhancements. I'll leave my prior answer for 1.0.1 below the fold.

编辑:1.0.2 版带来了一些重大的变化/增强。我将把我之前对 1.0.1 的回答留在首屏下方。

I put a sample wrapper class that you can either deploy as-is, or use to learn JPSVolumeButtonHandler's hopefully proper use in a separate Github repository real quick here.

我放了一个示例包装器类,您可以按原样部署,也可以用来学习 JPSVolumeButtonHandler 的希望在单独的 Github 存储库中的正确使用。

Here's how the wrapper is meant to be used (I'll add this to the repository as soon as I get to it):

以下是包装器的使用方式(我会尽快将其添加到存储库中):

  1. The singleton class has two flags: isInUseand isOn. isInUseis meant to be set in some sort of general app settings and switches button support on and off in general. So, no matter any other values in the class, if this is falsenothing will happen when the user presses a volume button and the implementation makes sure as much as possible to keep things clean and not affect system volume level unnecessarily. (Read the issue mentioned in the README for what can happen, when button support is switched on for the first time.) isOnis meant to be trueexactly for the duration that the button is needed. You can switch it on and off without regard to the present value of isInUse.

  2. In whichever view you initialize the action that's supposed to happen when a volume button gets pressed, set the action like so:

    PhysicalButton.shared.action = { /* do something */ }

  1. 单例类有两个标志:isInUseisOnisInUse旨在在某种常规应用程序设置中进行设置,并通常打开和关闭按钮支持。因此,无论类中的任何其他值如何,如果false用户按下音量按钮时都不会发生任何事情,并且实现会尽可能确保保持干净并且不会不必要地影响系统音量级别。(阅读README了解会发生什么,当按钮的支持上首次切换提到的问题) isOn,就是要true准确的时间是需要的按钮。您可以打开和关闭它,而无需考虑 的当前值isInUse

  2. 无论您在哪个视图中初始化按下音量按钮时应该发生的动作,都可以像这样设置动作:

    PhysicalButton.shared.action = { /* 做某事 */ }

The action has type () -> Void. Until you initialize the action, nothing will break. Just nothing will happen. This defensive functionality was important to me as the view that uses volume button support would only be created after button support is set up.

该操作的类型为() -> Void。在您初始化操作之前,不会有任何中断。只是什么都不会发生。这种防御功能对我很重要,因为使用音量按钮支持的视图只会在设置按钮支持后创建。

For seeing things in action, you can download the appthat I am using this in real quick for free. The settings manipulate "Physical button support" in general. The main Stopwatch view is the one to actually switch button handling on when entering the view, and off on leaving it. If you find the time, you'll also find an important note there in Settings > User Guide > Option: Physical Button Support:

要查看实际情况,您可以免费下载我正在使用的应用程序。这些设置通常操纵“物理按钮支持”。主秒表视图是在进入视图时实际打开按钮处理并在离开视图时关闭的视图。如果您找到时间,您还会在“设置”>“用户指南”>“选项:物理按钮支持”中找到一条重要说明:

In exceptional circumstance, the app may not get a chance to properly switch volume button handling off outside the Stopwatch view...

在特殊情况下,应用程序可能无法在秒表视图之外正确关闭音量按钮处理...

I'll add the full note to the Github README.md. Feel free to adapt and reuse it, if it's relevant in your case.

我会将完整的注释添加到 Github README.md 中。如果它与您的情况相关,请随意调整和重用它。

The circumstances aren't actually that exceptional and I haven't fully figured out what's wrong. When the user kills the app (or you just stop your app from within Xcode) while volume buttons are on, physical button support may not properly be removed from the OS. Thus, you can end up with two internal handler instances, only one of which you have control over. So, then every button tap results in two or even more calls to the action routine. My wrapper has some guardian code to prevent too rapid an invocation of the button. But that's only a partial solution. The fix need to go into the underlying handler, which I regrettably still have too little an understand of to try to fix things myself.

情况实际上并没有那么特殊,我还没有完全弄清楚出了什么问题。当用户在音量按钮打开的情况下终止应用程序(或者您只是从 Xcode 中停止应用程序)时,物理按钮支持可能无法从操作系统中正确删除。因此,您最终可以得到两个内部处理程序实例,您只能控制其中一个。因此,每次点击按钮都会导致对动作例程的两次甚至更多次调用。我的包装器有一些保护代码来防止按钮调用太快。但这只是部分解决方案。修复需要进入底层处理程序,遗憾的是我对它的了解仍然太少,无法尝试自己修复。



OLD, FOR 1.0.1:

旧的,对于 1.0.1:

In particular, my interest was in a Swift solution. The code is in Objective-C. To save someone some research, this is all I did using Cocoapods (for dummies like me):

特别是,我对 Swift 解决方案感兴趣。代码在 Objective-C 中。为了节省一些研究,这就是我使用 Cocoapods 所做的一切(对于像我这样的傻瓜):

  1. Add pod 'JPSVolumeButtonHandler'to the podfile
  2. Run pod installon the command line
  3. Add #import <JPSVolumeButtonHandler.h>to the bridging header file
  4. Set up callbacks for the volume up and down buttons like so:

    let volumeButtonHandler = JPSVolumeButtonHandler(
        upBlock: {
            log.debug("Volume up button pressed...")
            // Do something when the volume up button is pressed...
        }, downBlock: {
            log.debug("Volume down button pressed...")
            // Do something else for volume down...
        })
    
  1. 添加pod 'JPSVolumeButtonHandler'到 podfile
  2. pod install在命令行上运行
  3. 添加#import <JPSVolumeButtonHandler.h>到桥接头文件
  4. 为音量增大和减小按钮设置回调,如下所示:

    let volumeButtonHandler = JPSVolumeButtonHandler(
        upBlock: {
            log.debug("Volume up button pressed...")
            // Do something when the volume up button is pressed...
        }, downBlock: {
            log.debug("Volume down button pressed...")
            // Do something else for volume down...
        })
    

That's it. The rest is optional.

就是这样。其余的是可选的。



In my case, I wanted to enable overlaying physical button pushes with virtual on-screen buttons just for select views, while making sure to block as little of the normal button functions as possible (so that the user can run music in the background and adjust its volume in the rest of the app just fine). I ended up with a mostly singleton class as follows:

在我的例子中,我想为选择视图启用带有虚拟屏幕按钮的覆盖物理按钮按下,同时确保尽可能少地阻止正常按钮功能(以便用户可以在后台播放音乐并调整它在应用程序的其余部分中的音量就好了)。我最终得到了一个主要是单例的类,如下所示:

class OptionalButtonHandler {

  static var sharedInstance: OptionalButtonHandler?

  private var volumeButtonHandler: JPSVolumeButtonHandler? = nil
  private let action: () -> ()

  var enabled: Bool {
    set {
        if !enabled && newValue {
            // Switching from disabled to enabled...
            assert(volumeButtonHandler == nil, "No leftover volume button handlers")
            volumeButtonHandler = JPSVolumeButtonHandler(upBlock: {
                log.debug("Volume up button pressed...")
                self.action()
                }, downBlock: {
                    log.debug("Volume down button pressed...")
                    self.action()
            })
        } else if enabled && !newValue {
            log.debug("Disabling physical button...")
            // The other way around: Switching from enabled to disabled...
            volumeButtonHandler = nil
        }
    }
    get { return (volumeButtonHandler != nil) }
  }

  /// For one-time initialization of this otherwise singleton class.
  static func initSharedInstance(action: () -> ()) {
      sharedInstance = OptionalButtonHandler(action: action)
  }

  private init(action: () -> ()) {
      self.action = action
  }
}

There is just one common action for both up and down volume buttons here. The initSharedInstance()was necessary, because my action included references to a UI element (a view) that would only be set up at some user-dependent point after app launch.

这里只有一个用于增大和减小音量按钮的常见操作。这initSharedInstance()是必要的,因为我的操作包括对 UI 元素(视图)的引用,该元素只会在应用程序启动后的某个用户依赖点设置。

One-time set up like so:

一次性设置如下:

OptionalButtonHandler.initSharedInstance({
    // ...some UI action
})

Enable/disable selectively simply like so:

像这样有选择地启用/禁用:

OptionalButtonHandler.sharedInstance!.enabled = true  // (false)

(Notice that my code logic makes sure that .enabledis never accessed before initSharedInstance().)

(请注意,我的代码逻辑确保.enabled之前从未访问过initSharedInstance()。)

I am running Xcode 7.3 and iOS 9.3.2 on the (required!) test device.

我在(必需的!)测试设备上运行 Xcode 7.3 和 iOS 9.3.2。

Looking forward to learning how Apple feels about overloading their precious volume buttons. At least my app makes sure to be minimally invasive and the button use really makes sense. It's not a camera app, but comparable apps have used physical volume buttons before (less nicely even).

期待了解 Apple 对其宝贵的音量按钮超载的感受。至少我的应用程序确保是微创的,按钮的使用真的很有意义。它不是相机应用程序,但类似的应用程序以前使用过物理音量按钮(甚至不太好)。

回答by iRavi iVooda

Okay,

好的,

So I saw your solutions and don't exactly know whether Apple is going to reject or accept using AVSystemController_SystemVolumeDidChangeNotification. But I have a work around.

所以我看到了你的解决方案,并不完全知道 Apple 是否会拒绝或接受使用AVSystemController_SystemVolumeDidChangeNotification. 但我有一个解决办法。

Use UISliderof MPVolumeViewfor registering for any changes in volume by the iPhone hardware like this

使用UISliderMPVolumeView由iPhone硬件这样的体积注册的任何变化

MPVolumeView *volumeView = [[MPVolumeView alloc] initWithFrame:CGRectZero];

for (UIView *view in [volumeView subviews]) {
    if ([view.class.description isEqualToString:@"MPVolumeSlider"]){
        self.volume_slider = (UISlider*)view;
        break;
    }
}
[volumeView sizeToFit];
#THIS IS THE MAIN LINE. ADD YOUR CALLBACK TARGET HERE
[self.volume_slider addTarget:self action:@selector(volumeListener:) forControlEvents:UIControlEventValueChanged];
[self addSubview:volumeView];
[volumeView setAlpha:0.0f];

-(void)volumeListener:(NSNotification*)notification {
     #UPDATE YOUR UI ACCORDING OR DO WHATEVER YOU WANNA DO.
     #YOU CAN ALSO GET THE SOUND STEP VALUE HERE FROM NOTIFICATION.
}

Let me know if this helps anyone.

如果这对任何人有帮助,请告诉我。

回答by rpetrich

If you are willing to dip into the private API, I have a patch to Wolf3dthat adds exactly the functionality you are looking for. It uses the private AVSystemControllerclass and some hidden methods on UIApplication

如果您愿意深入了解私有 API,我有一个 Wolf3d 补丁,可以准确添加您正在寻找的功能。它使用私有AVSystemController类和一些隐藏的方法UIApplication