iOS 音量键的监听的方案
在iOS上,如何监听用户的音量键行为
需求
用户点击音量键的两个需求:
- 音量达到最大时,用户继续点击音量增加键,支持进一步放大音量;
- 音量达到最小时,用户继续点击音量减小键,支持让音量达到无声;
拆解
- 监听
- 增强
监听,直接祭出最佳方案
监听 iOS 系统私有通知: @“SystemVolumeDidChange”
以 iOS 15.0 为界,iOS 15.0 前后的通知详情 userInfo 有区别:
/// > iOS 15.0
userInfo = {
AudioCategory = PhoneCall;
Reason = ExplicitVolumeChange;
SequenceNumber = 1498;
Volume = "0.125";
}
/// < iOS 15.0
userInfo = {
AudioCategory = PhoneCall;
AudioVolume = 1;
AudioVolumeChangeReason = ExplicitVolumeChange;
UserVolumeAboveEUVolumeLimit = 0;
}
通知详情
这个通知不只在音量变化时会触发,还会在音量相关类别变化时收到此通知, userInfo 中包含“AudioCategory”、“Reason” 、“SequenceNumber” 、“Volume” 四个字段:
AudioCategory
这个字段指示当前音量所处的类别,主要已知包含几种情况:
- AudioCategory = "Audio/Video"
主要是指媒体音量,即 AVAudioSessionMode 切换为 AVAudioSessionModeDefault 、AVAudioSessionModeMoviePlayback、 AVAudioSessionModeSpokenAudio 、AVAudioSessionModeVideoRecording 这四个之一时。 - AudioCategory = PhoneCall
主要是指通话音量,即 AVAudioSessionMode 切换为 AVAudioSessionModeGameChat 、AVAudioSessionModeVideoChat 、AVAudioSessionModeVoiceChat 、AVAudioSessionModeVoicePrompt 这四个之一时。例如我们在使用 PSTN 通话时,调整音量,收到的通知中 AudioCategory = PhoneCall。 - AudioCategory = Ringtone
主要是指 PSTN 的来电响铃,即收到一个 PSTN 来电时,如果此时系统是播放铃音的提醒方式,则收到通知中 AudioCategory = Ringtone。 - AudioCategory = Alarm
主要是指闹铃的铃音响起 - AudioCategory = VoiceCommand
主要是指 siri 的语音播报 - AudioCategory = FindMyPhone
主要是指点击控制面板中调整找回设备的方式时
Reason
这个字段指示收到当前这个通知的原因,主要包含
- Reason = CategoryChange
主要是指 “AudioCategory”字段发生变化的时候,此时实际并没有音量的调整 - Reason = RouteChange
主要是指当前的输出发生变化的时候,例如麦克风播放改为听筒播放。此时实际并没有音量的调整 - Reason = ExplicitVolumeChange
主要是指实际的音量调整,例如我们点按物理按键的音量加减键,或者控制面板中调节滑动音量槽。
SequenceNumber
这个字段指示当前通知的序号,由于通知的重复和无序, 要注意特殊情况:
- 有些情况下会收到连续几个通知,内容相同,但是序号递增
- 物理按键或控制面板调整音量时,会收到两个序号和内容都相同的通知
- 有些情况下会 1、2、1、2 这样顺序的通知
Volume
这个字段指示当前上下文环境下的音量值。如果是音量调整后的通知,则只是调整后最新的音量值。
回到需求本身,当我们判断到音量达到最大值 1 时,还能收到 Reason = ExplicitVolumeChange,并且 Volume = 1 的通知,就说明识别到了用户还需要进一步放大音量的场景;当我们判断到通话音量达到最小值(通话音量最小值 0.0625,媒体音量最小值 0),还能收到 Reason = ExplicitVolumeChange,并且 Volume = 0.0625 的通知,说明识别到了用户需要完全无声的场景 。
代码
第一步,监听通知
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(systemVolumeDidChange:)
name:@"SystemVolumeDidChange"
object:nil];
第二步,响应通知的方法
- (void)systemVolumeDidChange:(NSNotification *)notification {
NSDictionary *userInfo = [notification userInfo];
NSString *reason = [userInfo objectForKey:@"Reason"];
float volume = [[userInfo objectForKey:@"Volume"] floatValue];
NSString *audioCategory = [userInfo objectForKey:@"AudioCategory"];
if ([reason isEqualToString:@"ExplicitVolumeChange"]) {
if ([audioCategory isEqualToString:@"Audio/Video"]) {
if (volume >= 1.0) {
[self amplifyVolume];
} else if (volume <= 0.0625) {
[self muteVolume];
}
}
}
}
第三步,具体实现
放大音量
- (void)amplifyVolume {
// 使用应用内部的声音增强功能来进一步放大音量
// 1. 获取当前正在播放的音频会话
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
// 2. 使用某种算法增加音频数据的振幅(这里只是一个示例)
// 例如:audioData = audioData * amplificationFactor;
// 3. 重新播放增强后的音频
// 假设你有一个名为 'audioPlayer' 的 AVAudioPlayer 对象
// [audioPlayer play];
}
音量调整为无声
- (void)muteVolume {
// 使用代码将音量设置为静音
// 假设你有一个名为 'audioPlayer' 的 AVAudioPlayer 对象
audioPlayer.volume = 0.0;
}
彩蛋
- iOS 端可用的打断通知 AVAudioSessionInterruptionNotification 对打断原因没有详细信息, 而这个音量变化通知中的 AudioCategory 字段可以为音频打断提供更多参考信息
- 音量键的监听可以很方便用于调试,不需要在视图上添加 UI 组件,只需通过监听并实现响应方法,就可以通过物理按键调试不同场景的切换。