在iOS 8上捕获音量向上/向下按钮按下的最干净的方式

qacovj5a  于 2023-03-31  发布在  iOS
关注(0)|答案(9)|浏览(194)

iOS 8 上捕获音量增大/减小按钮按下的最佳/最干净的方法是什么?
理想情况下,我想捕捉按键,也防止系统音量的变化(或至少,防止音量变化HUD显示)。
有一些旧的答案使用了过时的方法,在iOS 8上似乎根本不起作用。这个iOS 8 specific one也不起作用。
这个RBVolumeButtons开源类似乎也不能在iOS 8上工作。

lnxxn5zx

lnxxn5zx1#

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

此代码检测用户的显式音量更改操作,就像您没有检查显式操作一样,此函数将定期自动调用。
此代码不会阻止系统音量更改。

baubqpgj

baubqpgj2#

先添加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);
  }
}
rqdpfwrv

rqdpfwrv3#

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

....

axkjgtzd

axkjgtzd4#

这是一个2部分的答案,它们是独立的,在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)
ni65a41a

ni65a41a5#

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
v440hwme

v440hwme6#

合并解决方案(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上面的伟大回应。

sczxawaw

sczxawaw7#

Swift 5 / iOS 15
在iOS 15上,通知名称重命名为 SystemVolumeDidChange

// hide volume indicator
let volumeView = MPVolumeView(frame: CGRect(x: -CGFloat.greatestFiniteMagnitude, y: 0.0, width: 0.0, height: 0.0))
self.view.addSubview(volumeView)

添加通知观察者

NotificationCenter.default.addObserver(self, selector: #selector(volumeChanged), name: NSNotification.Name(rawValue: "SystemVolumeDidChange"), object: nil)

监听音量变化

private var notificationSequenceNumbers = Set<Int>()
@objc func volumeChanged(notification: Notification) {
    if let userInfo = notification.userInfo {
        if let volumeChangeType = userInfo["Reason"] as? String,
           volumeChangeType == "ExplicitVolumeChange", let sequenceNumber = userInfo["SequenceNumber"] as? Int {
            DispatchQueue.main.async {
                if !self.notificationSequenceNumbers.contains(sequenceNumber) {
                    self.notificationSequenceNumbers.insert(sequenceNumber)
                    // handle volume change
                }
            }
        }
    }
}

注意事项:

  1. iOS 15上的通知会被调用两次,即使你按一次音量按钮,但它们的SequenceNumber是相等的,所以我们可以使用Set来处理通知一次。
    1.通知回调不在主线程上,所以我们需要使用DispatchQueue,以便Set一致。
h43kikqp

h43kikqp8#

好的,请参阅音频会话服务参考以了解更多信息。您需要使用AudioSessionInitialize启动音频会话,然后使用AudioSessionSetActive使其处于活动状态,使用AudioSessionAddPropertyListener侦听音量的变化,并传递具有类型AudioSessionPropertyListener的回调。
这个网站写得很好:http://fredandrandall.com/blog/2011/11/18/taking-control-of-the-volume-buttons-on-ios-like-camera/

xmq68pz9

xmq68pz99#

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
}

相关问题