【问题标题】:Cleanest way of capturing volume up/down button press on iOS 8在 iOS 8 上捕获音量增大/减小按钮的最简洁方式
【发布时间】:2015-03-27 10:25:00
【问题描述】:

iOS 8 上捕捉音量增大/减小按钮按下的最佳/最干净的方法是什么?

理想情况下,我想捕捉按键并防止系统音量发生变化(或者至少,防止显示音量变化HUD)。


有一些旧的答案使用过时的方法,似乎在 iOS 8 上根本不起作用。这个iOS 8 specific one 也不起作用。

这个RBVolumeButtons 开源类似乎也不适用于 iOS 8。

【问题讨论】:

  • 这是针对越狱应用还是企业应用?如果没有,Apple 将无法通过此审核。
  • 不,这是针对普通 App Store 的。我读到苹果现在批准在某些特定情况下捕获音量按钮(例如,将它们用作相机快门,请参阅Camera+)。
  • 在这里查看我对重复问题的回答:stackoverflow.com/a/37360733/893774 我相信 JPSVolumeButtonHandler 是目前最干净的方式,特别是自最近的 1.0.1 修复以来。
  • 我同意@marco,JPSVolumeButtonHandler 在 iOS 8.x 和 iOS 9.x 上运行良好。我有一个答案说明了这一点,但没有警告就被删除了。 ¯_(ツ)_/¯

标签: ios ios8 volume


【解决方案1】:

好的,请参阅音频会话服务参考以获取更多信息。您需要使用 AudioSessionInitialize 启动音频会话,然后使用 AudioSessionSetActive 使其处于活动状态,使用 AudioSessionAddPropertyListener 监听音量的变化并传递具有 AudioSessionPropertyListener 类型的回调。

这个网站写得很好:http://fredandrandall.com/blog/2011/11/18/taking-control-of-the-volume-buttons-on-ios-like-camera/

【讨论】:

  • 对不起,我刚刚看到你链接到的 github
  • 是的,正如我所说的那样,这种方法似乎不适用于 iOS 8(但 Camera+ 继续在 iOS 8 上提供此功能)。
【解决方案2】:

首先添加 AVFoundationMediaPlayer Framework 然后你可以使用下面的代码来检测向上/向下按钮的按下,

-(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"]) {        
      float volumeLevel = [[MPMusicPlayerController applicationMusicPlayer] volume];
      NSLog(@"volume changed! %f",volumeLevel);
  }
}

【讨论】:

  • 不错的解决方案,但请记住,如果音量已经达到最大值,它不会检测到“提高音量”按钮(如果已经静音,则不会检测到“降低”按钮
  • 只是想知道,这会不会违反苹果的条款和条件,正如这里提到的stackoverflow.com/questions/29923664/…
  • 即使我也想知道同样的情况,是否可以以编程方式反转音量变化? (使音量保持在原来的水平
  • @Eliktz 这种情况有什么解决办法吗?)
  • @daisura99 该指南现已被删除,并且允许此行为,至少用于触发拍照
【解决方案3】:

对于 Swift,您可以在 viewController 类中使用下一个代码:

let volumeView = MPVolumeView(frame: CGRectMake(-CGFloat.max, 0.0, 0.0, 0.0))
self.view.addSubview(volumeView)
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(volumeChanged(_:)), name: "AVSystemController_SystemVolumeDidChangeNotification", object: nil)

然后添加这个函数

func volumeChanged(notification: NSNotification) {

     if let userInfo = notification.userInfo {
        if let volumeChangeType = userInfo["AVSystemController_AudioVolumeChangeReasonNotificationParameter"] as? String {
            if volumeChangeType == "ExplicitVolumeChange" {
                // your code goes here
            }
        }
    }
}

这段代码检测用户显式的音量变化动作,如果你没有检查显式动作,这个函数会被自动周期性地调用。

此代码不会阻止系统音量变化

【讨论】:

  • 你知道如何防止系统显示音量hud吗?
  • 使用这个(iOS 12 到目前为止只测试过)出现奇怪的行为,当按下锁定按钮时也会触发。此外,要“防止”显示体积视图,请创建自己的 MPVolumeView,将其添加为子视图,并将其原点设置在可见视图之外。
  • 有没有办法检测长按音量键摇杆?
【解决方案4】:

对于 Swift 3: (记得加上:import MediaPlayer..)

    override func viewDidLoad() {
        super.viewDidLoad()

        let volumeView = MPVolumeView(frame: CGRect(x: 0, y: 40, width: 300, height: 30))
        self.view.addSubview(volumeView)
//      volumeView.backgroundColor = UIColor.red
        NotificationCenter.default.addObserver(self, selector: #selector(volumeChanged(notification:)),
                                               name: NSNotification.Name(rawValue: "AVSystemController_SystemVolumeDidChangeNotification"),
                                               object: nil)
    }


    func volumeChanged(notification: NSNotification) {

        if let userInfo = notification.userInfo {
            if let volumeChangeType = userInfo["AVSystemController_AudioVolumeChangeReasonNotificationParameter"] as? String {
                if volumeChangeType == "ExplicitVolumeChange" {
                    // your code goes here
                }
            }
        }
    }

....

【讨论】:

  • 如何查看音量增大或减小?
  • @NikKov if let newVolume = userInfo["AVSystemController_AudioVolumeNotificationParameter"] as?双 { print(newVolume) }
【解决方案5】:

Objective-C 版本(使用通知):

#import <MediaPlayer/MPVolumeView.h>
#import <AVFoundation/AVFoundation.h>

@interface ViewController () {
    UISlider *volumeViewSlider;
}

@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    [self startTrackingVolumeChanges];
}

- (void)dealloc
{
    [self stopTrackingVolumeChanges];
}

#pragma mark - Start Tracking Volume Changes

- (void)startTrackingVolumeChanges
{
    [self setupVolumeViewSlider];
    [self addObserver];
    [self activateAudioSession];
}

- (void)setupVolumeViewSlider
{
    MPVolumeView *volumeView = [[MPVolumeView alloc] init];
    for (UIView *view in [volumeView subviews]) {
        if ([view.class.description isEqualToString:@"MPVolumeSlider"]) {
            volumeViewSlider = (UISlider *)view;
            break;
        }
    }
}

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

- (void)activateAudioSession
{
    AVAudioSession *audioSession = [AVAudioSession sharedInstance];
    [audioSession setCategory:AVAudioSessionCategoryPlayback withOptions:AVAudioSessionCategoryOptionMixWithOthers error:nil];
    NSError *error;
    BOOL success = [audioSession setActive:YES error:&error];
    if (!success) {
        NSLog(@"Error activating audiosession: %@", error);
    }
}

#pragma mark - Observing Volume Changes

- (void)volumeChanged:(NSNotification *)notification
{
    NSString *volumeChangeType = notification.userInfo[@"AVSystemController_AudioVolumeChangeReasonNotificationParameter"];
    if ([volumeChangeType isEqualToString:@"ExplicitVolumeChange"]) {
        float volume = volumeViewSlider.value;
        NSLog(@"volume = %f", volume);
    }
}

#pragma mark - Stop Tracking Volume Changes

- (void)stopTrackingVolumeChanges
{
    [self removeObserver];
    [self deactivateAudioSession];
    volumeViewSlider = nil;
}

- (void)removeObserver
{
    [[NSNotificationCenter defaultCenter] removeObserver:self name:@"AVSystemController_SystemVolumeDidChangeNotification" object:nil];
}

- (void)deactivateAudioSession
{
    dispatch_async(dispatch_get_main_queue(), ^{
        NSError *error;
        BOOL success = [[AVAudioSession sharedInstance] setActive:NO error:&error];
        if (!success) {
            NSLog(@"Error deactivating audiosession: %@", error);
        }
    });
}

@end

【讨论】:

    【解决方案6】:

    这是一个两部分的答案,它们是独立的,在 Swift 5 中。

    要监听音量触发事件,

    import MediaPlayer
    
    // Observe in eg. viewDidLoad
    let volumeChangedSystemName = NSNotification.Name(rawValue: "AVSystemController_SystemVolumeDidChangeNotification")
    NotificationCenter.default.addObserver(self, selector: #selector(volumeChanged), name: volumeChangedSystemName, object: nil)
    
    @objc private func volumeChanged(notification: NSNotification) {
        guard
            let info = notification.userInfo, 
            let reason = info["AVSystemController_AudioVolumeChangeReasonNotificationParameter"] as? String,
            reason == "ExplicitVolumeChange" else { return }
    
        // Handle it
    }
    

    要隐藏系统音量控制,

    // Add the view but not visible
    let volumeView = MPVolumeView(frame: CGRect(x: -CGFloat.greatestFiniteMagnitude, y: 0, width: 0, height: 0))
    view.addSubview(volumeView)
    

    【讨论】:

    • 即使达到最大/最小音量并隐藏系统音量弹出窗口,此解决方案也能正常工作。
    • 如果我按住音量键,音量变化一级,然后有大约300ms的延迟,然后音量变化非常快。松开音量按钮后,音量停止变化。这足以获得“按钮按下”和“按钮释放”类型的事件,但我想知道是否有某种方法可以缩短第一步和快速更改之间的延迟。这意味着我不必等待这么长时间才能知道按钮是否已被释放。我猜这是不可能的,但我只是想我会问。 ;-)
    【解决方案7】:

    Swift 5 / iOS 13

    您已经需要MPVolumeView 以使系统隐藏音量更改 HUD。我发现的最简单、最可靠的解决方案是观察 MPVolumeView 本身的变化。

    设置期间,通常在viewDidLoad()

    // Create offscreen MPVolumeView
    let systemVolumeView = MPVolumeView(frame: CGRect(x: -CGFloat.greatestFiniteMagnitude, y: 0, width: 0, height: 0))
    myContainerView.addSubview(systemVolumeView)
    let systemVolumeSlider = systemVolumeView.subviews.first(where:{ $0 is UISlider }) as? UISlider
    
    // Observe volume changes (including from hardware buttons):
    systemVolumeSlider.addTarget(self, action: #selector(volumeDidChange), for: .valueChanged)
    

    响应音量变化:

    @objc func volumeDidChange() {
        // Handle volume change
    }
    

    【讨论】:

      【解决方案8】:

      组合解决方案(使用 Swift 5.2 / iOS 14 测试)

      • 确保将必要的框架导入到文件中;
      import Combine
      import MediaPlayer
      
      • 在函数外部为您的组合 AnyCancellable 设置一个变量;

      var volumeCancellable: AnyCancellable?

      • 在与您的需求相关的任何功能(viewDidLoad 或其他地方)中,配置组合订阅者;
      volumePublisher = NotificationCenter.default
         .publisher(for: NSNotification.Name(rawValue: "AVSystemController_SystemVolumeDidChangeNotification"))
         .compactMap { $0.userInfo!["AVSystemController_AudioVolumeChangeReasonNotificationParameter"] as? String }
         .filter { $0 == "ExplicitVolumeChange" }
         .sink(receiveValue: { (val) in
             // Do whatever you'd like here
         })
      
      • 在与您的需求相关的任何功能中(同样,viewDidLoad 或其他地方),设置音量控制和屏幕外位置;
      let volumeView = MPVolumeView(frame: CGRect(x: -CGFloat.greatestFiniteMagnitude, y: 0, width: 0, height: 0))
      view.addSubview(volumeView)
      

      基于上述@samwize 的好评。

      【讨论】:

      • 此解决方案的每次触发事件之间的延迟要短得多。因此,您可以检测到按钮何时释放,而不会延迟 0.5 秒。太棒了!
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-30
      • 1970-01-01
      • 1970-01-01
      • 2014-01-31
      • 1970-01-01
      相关资源
      最近更新 更多