有时在项目中需要监听用户是否按下了物理声音键,然后来做某些操作,如:你自定义了一个照相功能,希望用户按下声音按键时也能进行拍照,苹果自带的照相机就有这种功能.
监听物理声音键是否按下的方法有很多中,我在这里只讲两种,也是我比较熟悉的
一、通过 NSNotificationCenter 观察一个叫做 @“AVSystemController_SystemVolumeDidChangeNotification” 的通知,
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(volumeChanged:) name:@"AVSystemController_SystemVolumeDidChangeNotification" object:nil]; - (void)volumeChanged:(NSNotification *)notification { float volume = [[[notification userInfo] objectForKey:@"AVSystemController_AudioVolumeNotificationParameter"] floatValue]; NSLog(@"current volume = %f", volume); }
二、通过为AudioSession添加一个监听者来实现,
-(BOOL)addHardKeyListener{ OSStatus s = AudioSessionAddPropertyListener(kAudioSessionProperty_CurrentHardwareOutputVolume, hardKeyListener, "kAudioSessionProperty_CurrentHardwareOutputVolume"); return s==kAudioSessionNoError; } void hardKeyListener( void *inClientData, AudioSessionPropertyID inID, UInt32 inDataSize, const void *inData ){ if (inID != kAudioSessionProperty_CurrentHardwareOutputVolume) { return; } NSLog(@"%s",inClientData); }
对于上面这种方式,如果你直接这样,那当你按下物理音量键时不会有任何的反应,你必须在调用它之前,手动初始化audioSession
你可以
AudioSessionInitialize(NULL, kCFRunLoopDefaultMode, interruptionListenerCallback, "AudioSessionInitialize");
或者直接
AudioSessionInitialize(NULL, NULL, NULL, NULL);
当你完成了上面的任何一种方式后,你就可以很好的捕捉到音量键是否按下了,但此时你会发现,当你按下音量键时,系统自带的,反映音量设置的view会
出现,试想下,当你的用户通过物理音量键操作你的程序,比如拍照,你还给人家显示个系统的音量设置键,那用户体验可想而知了,
那怎么解决呢?
那个系统自带的音量设置view 其实就是个苹果自定义的MPVolumeView,没当音量键按下时,它都会出现,但如果当前显示的正好有一个这样的东西,那这个系统的音量设置view 就不会反客为主,自己显示出来了,而是显示你自己定义的
呵呵,我就是不需要这个,那好办,自定义一个,然后加到看不到的地方不就得了,
如下:
MPVolumeView *volumeView = [[MPVolumeView alloc]initWithFrame:CGRectMake(-100, -100, 100, 100)]; [self.view addSubview:volumeView];
但此时,你必须激活audioSession,不然得话它还是会自己显示得
加上代码:
AudioSessionSetActive(true);
三、完成上面得东西后,你基本上可以正常捕捉到音量键得按下,但有一点要注意:audioSession在你得程序进入后台后会变为不激活状态,当你再次回到前台后,你得程序得audioSession其实是没有激活得,此时,你按下音量键,那系统得那个音量设置view 就又出来了,所以你应该添加两个系统通知,当程序进入后台时
AudioSessionSetActive(false); 当程序进入前台时 AudioSessionSetActive(true);
而且,如果捕捉音量键得按下只在某个画面有效,那当该画面不再示但前显示得画面示应该移除捕捉动作
下面是一些测试用得代码,没有什么逻辑关联性
// // ViewController.m // AudioSessionDemo // // Created by PSH_Chen_Tao on 7/18/13. // Copyright (c) 2013 wolfman. All rights reserved. // #import "ViewController.h" #import <MediaPlayer/MediaPlayer.h> @interface ViewController () @end @implementation ViewController @synthesize imagePickerController; - (void)viewDidLoad { [super viewDidLoad]; // 获得当前的音量,因为按物理音量键,声音会发生改变,我们可以在他每次按下音量键时都通过下面的初始音量值来还原它. float initVolume = [MPMusicPlayerController applicationMusicPlayer].volume; NSLog(@"%f ",initVolume); // Do any additional setup after loading the view, typically from a nib. AudioSessionInitialize(NULL, kCFRunLoopDefaultMode, interruptionListenerCallback, "AudioSessionInitialize"); //通过捕捉物理音量键按下时产生的通知来对其进行监听 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(volumeChanged:) name:@"AVSystemController_SystemVolumeDidChangeNotification" object:nil]; [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(enterBackground:) name:UIApplicationDidEnterBackgroundNotification object:nil]; [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(enterForground:) name:UIApplicationDidBecomeActiveNotification object:nil]; //避免按下音量键时,其系统自带的音量设置画面会出现 MPVolumeView *volumeView = [[MPVolumeView alloc]initWithFrame:CGRectMake(-100, -100, 100, 100)]; [self.view addSubview:volumeView]; } //进入后台,释放AudioSession -(void)enterBackground:(NSNotification *)n{ AudioSessionSetActive(false); } //进入前台,激活AudioSession -(void)enterForground:(NSNotification *)n{ AudioSessionSetActive(true); } - (void)volumeChanged:(NSNotification *)notification { float volume = [[[notification userInfo] objectForKey:@"AVSystemController_AudioVolumeNotificationParameter"] floatValue]; NSLog(@"current volume = %f", volume); } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } void interruptionListenerCallback( void *inClientData, UInt32 inInterruptionState ){ if (inInterruptionState == kAudioSessionBeginInterruption) { NSLog(@"begin interruption"); } if (inInterruptionState == kAudioSessionEndInterruption) { NSLog(@"end interruption"); } NSLog(@"%s",inClientData); } -(BOOL)isMuted{ CFStringRef route; UInt32 routeSize = sizeof(route); OSStatus s = AudioSessionGetProperty(kAudioSessionProperty_AudioRoute, &routeSize, &route); if (s==kAudioSessionNoError) { if (route==NULL || CFStringGetLength(route)==0) { return YES; } } return NO; } -(BOOL)addMutedListener{ OSStatus s = AudioSessionAddPropertyListener(kAudioSessionProperty_AudioRouteChange, mutedListener, "kAudioSessionProperty_AudioRouteChange"); return s==kAudioSessionNoError; } void mutedListener( void *inClientData, AudioSessionPropertyID inID, UInt32 inDataSize, const void *inData ){ if (inID != kAudioSessionProperty_AudioRouteChange) { NSLog(@"inID != kAudioSessionProperty_AudioRouteChange %s",inClientData); return; } NSLog(@"%s",inClientData); } -(BOOL)addHardKeyListener{ OSStatus s = AudioSessionAddPropertyListener(kAudioSessionProperty_CurrentHardwareOutputVolume, hardKeyListener, "kAudioSessionProperty_CurrentHardwareOutputVolume"); return s==kAudioSessionNoError; } void hardKeyListener( void *inClientData, AudioSessionPropertyID inID, UInt32 inDataSize, const void *inData ){ if (inID != kAudioSessionProperty_CurrentHardwareOutputVolume) { return; } NSLog(@"%s",inClientData); } - (IBAction)addListener:(id)sender { [self addMutedListener]; [self addHardKeyListener]; } - (IBAction)removeListener:(id)sender { AudioSessionRemovePropertyListenerWithUserData(kAudioSessionProperty_CurrentHardwareOutputVolume, hardKeyListener, "kAudioSessionProperty_CurrentHardwareOutputVolume"); } -(void)viewWillAppear:(BOOL)animated{ [super viewWillAppear:animated]; // AudioSessionInitialize(NULL, NULL, NULL, NULL); AudioSessionSetActive(true); } -(void)viewWillDisappear:(BOOL)animated{ [super viewWillDisappear:animated]; AudioSessionSetActive(false); } @end