iOS的AVFoundation框架提供了基本的音视频播放工具,我们基本上可以靠其中提供的类完成绝大部分的音视频播放任务。但是在音频播放的输出音量的处理上,苹果的策略比较保守。尽管AVPlayer和AVPAudiolayerzhe这些类提供了音量调节功能,但这些音量控制属于App级别的控制。好处就是音量调节独立于系统音量,调节大小时不会影响系统音量。但有时候我们可能希望修改系统音量,以免在调节声音的时候,如果系统音量过小,App调节音量效果不明显。一般来说要调节系统音量会有以下方法:

请注意:修改系统音量无法在模拟器上看到效果,必须使用真机调试才能看到效果!

使用MPVolumeView

这个方法是苹果官方推荐的方法。MPVolumeView是Media Player Framework中的一个UI组件,直接包含了对系统音量和Airplay设备的音频镜像路由的控制功能。其中包含一个MPVolumeSlider的subview用来控制音量。这个MPVolumeSlider是一个私有类,我们无法手动创建此类,但这个类是UISlider的子类。MPVolumeView的使用很简单,只需要将其加入到一个父视图中,给予父视图合适的大小,再创建MPVolumeView示例,将其加入到父视图中即可,苹果官方的文档1中有示例代码可以参考。

这个方法的缺点如下:

  • UI可定制的的程度低。 MPVolumeView只提供了有限的几个方法来定制其中的Slider和Route Button的样式,而且基本上只能靠换图片解决。如果你想把Slider操作换成Button或者其他的UI组件,那是不可能的。
  • 没有额外的音量控制API。 目前为止没有发现iOS的公开API中有可以直接操作系统音量的,所以修改系统音量只能使用这个UI组件。

如果还想给UI加入手势操作来控制音量,这种直接使用MPVolumeView是做不到的,那么有没有什么方法可以绕过这限制呢?办法还是有的。

编程实现系统音量调节

上一小节我们提到了MPVolumeView这个组件中,有一个subview来控制音量,即MPVolumeSlider。其实我们可以通过遍历MPVolumeView实例的subviews来得到MPVolumeSlider的实例,从而通过这个UI组件来操作系统音量。

通过MPVolumeSlider的实例来操作系统音量

我们首先通过创建一个MPVolumeView,然后遍历找出MPVolumeSlider的实例。这个实例提供setValue:animated:方法来设置系统音量。我们也可以通过volumeSlider.value这个属性来获取当前的系统音量。具体的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
MPVolumeView *volumeView = [[MPVolumeView alloc] init];
UISlider* volumeViewSlider = nil;
for (UIView *view in [_instance.volumeView subviews]){
    if ([view.class.description isEqualToString:@"MPVolumeSlider"]){
        volumeViewSlider = (UISlider*)view;
        break;
    }
}
 
// retrieve system volume
float systemVolume = volumeViewSlider.value;
 
// change system volume, the value is between 0.0f and 1.0f
[volumeViewSlider setValue:1.0f animated:NO];
 
// send UI control event to make the change effect right now.
[volumeViewSlider sendActionsForControlEvents:UIControlEventTouchUpInside];

上面的代码演示如何获取和修改系统音量,注意音量取值为0到1之间的浮点数。

有问题!我不喜欢系统弹出音量提示

上面通过编程的方法可以很完美的调节系统音量,但是每次修改都会弹出系统提示框告知:

有时候这种提示我们未必会需要,那么怎么取消掉这个提示呢?实际上MPVolumeView没有提供任何接口来调节是否需要显示系统音量提示。但是我们发现一点:当MPVolumeView处在当前视图的层级之中时,系统就不会显示音量提示。那么事情好办了,我们只要确保两点:

  • MPVolumeView视图处在屏幕上看不见的地方,比如某个不透明视图的下方,或者本视图的非可见区域,一个常见的做法就是把该视图的frame设置为区域以外的地方,比如volumeView.frame = CGRectMake(-1000, -100, 100, 100);
  • 确保MPVolumeView视图的hidden属性值为NO。因为当hidden为YES时,同样会弹出提示。

还有问题,我修改了系统音量但是不是通过我的UI

另一个可能的情况就是用户自己通过硬件的音量调节按钮(位于设备侧边)来调节音量,这种情况会使得你的业务逻辑出现问题,因为你只为自己的App UI写了回调,那么怎么为硬件按钮的事件添加回调呢?我们可以使用Notification Center来完成。
这里只需要监听AVSystemController_SystemVolumeDidChangeNotification事件即可。具体代码如下:

  • 首先在资源载入阶段加入监听事件的代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
NSError *error;
// Active audio session before you listen to the volume change event.
// It must be called first.
// The old style code equivalent to the line below is:
//
// AudioSessionInitialize(NULL, NULL, NULL, NULL);
// AudioSessionSetActive(YES);
//
// Now the code above is deprecated in iOS 7.0, you should use the new
// code here.
[[AVAudioSession sharedInstance] setActive:YES error:&error];
 
// add event handler, for this example, it is `volumeChange:` method
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(volumeChanged:) name:@"AVSystemController_SystemVolumeDidChangeNotification" object:nil];
  • 然后实现事件回调方法
1
2
3
4
- (void)volumeChanged:(NSNotification *)notification
{
    // service logic here.
}
  • 最后记得在资源回收时取消掉事件监听
1
2
3
4
- (void)dealloc
{
    [[NSNotificationCenter defaultCenter] removeObserver:self name:@"AVSystemController_SystemVolumeDidChangeNotification" object:nil];
}

这样,每次用户使用硬件按钮调节音量的时候也会执行你写好的逻辑。

以上除了第一个方案以外,所有的解决方案都属于非官方的hack性质的方法,但是都没有调用私有API,所以没有被Apple审核拒掉的风险。

说法二:

在iOS设备中音量分为耳机音量和手机音量,二者相互独立:但是不管耳机还是 手机都是调用一种方法改变音量, 在设备上有耳机的时候改变耳机音量,没有耳机的时候改变手机音量。

在github中有一个开源项目:SystemVolumeNativeExtension . (点击获取链接)

在解压后找到 SystemVolumeNativeExtension/IOSVolumeLib/IOSVolumeLib/IOSVolumeLib.m 这个文件.

不用看太多,只需要关注:

float getVolumeLevel()
{
MPVolumeView *slide = [MPVolumeView new];
UISlider *volumeViewSlider; for (UIView *view in [slide subviews])
{
if ([[[view class] description] isEqualToString:@"MPVolumeSlider"])
{
volumeViewSlider = (UISlider *) view;
}
} float val = [volumeViewSlider value];
[slide release]; return val;
}

&

FREObject setVolume(FREContext ctx, void* funcData, uint32_t argc, FREObject argv[])
{
double newVolume;
FREGetObjectAsDouble(argv[0], &newVolume); [[MPMusicPlayerController applicationMusicPlayer] setVolume: newVolume]; return NULL;
}

如个你觉得这也有些繁琐: 那你直接使用:

[[MPMusicPlayerController applicationMusicPlayer] setVolume: newVolume];

newVolume的范围是 0 ~ 1;

简单说就是用这个即可设置耳机音量。

iOS编程修改系统音量的更多相关文章

  1. IOS修改系统音量

    #import <IOKit/IOKitLib.h> #import <IOKit/hidsystem/IOHIDLib.h> #import <IOKit/hidsys ...

  2. android小工具-系统音量管理器

    简介:调节系统音量的小工具,能够快捷的调节系统铃声,媒体音乐.闹钟和通话声音.你可能会想,手机自带的音量键还不够快捷吗?还得写个程序?首先,用音量键调音只能调节一种声音,像闹钟这种声音不能直接调.其次 ...

  3. iOS开发——运行时OC篇&使用运行时获取系统的属性:使用自己的手势修改系统自带的手势

    使用运行时获取系统的属性:使用自己的手势修改系统自带的手势 有的时候我需要实现一个功能,但是没有想到很好的方法或者想到了方法只是那个方法实现起来太麻烦,一或者确实为了装逼,我们就会想到iOS开发中最牛 ...

  4. android5.1修改系统默认音量

    在做定制需求的时候,需要修改系统通知的声音,将其禁用掉,避免第三方应用发送通知时,声音很大,吓着用户.索性就把通知声音关掉.下面就说说关闭声音的几种方法,以及修改系统默认声音的方法. 1. 直接修改系 ...

  5. iOS 获取系统音量

    //设置一个全局变量 UISilder * volumeViewSlider; #pragma mark - 获取系统音量 - (void)configureVolume { volumeView = ...

  6. iOS 设置系统音量和监听系统音量变化

    很简单的调用 首先在工程引入MediaPlayer.framework #import <MediaPlayer/MediaPlayer.h> 1. 获取系统音量 // 获取系统音量 MP ...

  7. Delphi编程获取系统当前进程、窗口句柄、文件属性以(转)

    Delphi编程获取系统当前进程.窗口句柄.文件属性以及程序运行状态. uses TLHelp32,PsAPI; (1)显示进程列表:procedure TForm1.Button2Click(Sen ...

  8. [译] 二、开始iOS编程之前,你还需要做什么?

    声明:本文翻译自AppCoda网站的文章:What You Need to Begin iOS Programming?,作者是创建者Simon Ng.如有异议,请联系博主.   更新:帖子已经重新被 ...

  9. IOS编程之多线程

    IOS编程之多线程 目录 概述——对多线程的理解 IOS中实现多线程的三种方式 NSThread 线程创建 线程的同步与锁 线程间的交互 线程的操作方法 NSOperation and NSOpera ...

随机推荐

  1. MAVEN 工程打包resources目录外的更多资源文件

    首先,来看下MAVENx项目标准的目录结构: 一般情况下,我们用到的资源文件(各种xml,properites,xsd文件等)都放在src/main/resources下面,利用maven打包时,ma ...

  2. poj1038

    题目大意:网络导航? 标准的web浏览器包含向前和向后浏览最近的页面的特性,有一个方法来实现这些用两个栈来跟踪页面达到向前和向后的移动,在这个问题里面,你被要求实现这些. 以下命令需要支持: BACK ...

  3. Event 元标签中的代码提示问题

    自定的事件可以利用Event元标签在支持该事件的类里面做绑定,绑定后FlashBuilder会有代码提示,以提示该类支持的事件类型 1 2 3 4 5 6 7 8 9 10 11 12 13 14 1 ...

  4. java关键字synchronized

    1.对于synchronized方法和synchronized块,一个线程访问时,其他线程可以访问此类的非synchronized方法或块,不能访问其他带synchronized的方法或块. 举例如下 ...

  5. [PWA] 7. First Cache when installed

    If you want your application works offline or lie-wifi. You need to use cache. API: Create Caches: c ...

  6. String.Split()函数

    我们在上次学习到了 String.Join函数(http://blog.csdn.net/zhvsby/archive/2008/11/28/3404704.aspx),当中用到了String.SPl ...

  7. 监控 Linux 性能的 18 个命令行工具

    http://www.oschina.net/translate/command-line-tools-to-monitor-linux-performance 1.Top-Linux进程监控 Lin ...

  8. Linux Top 命令解析 比较详细--转

    TOP是一个动态显示过程,即可以通过用户按键来不断刷新当前状态.如果在前台执行该命令,它将独占前台,直到用户终止该程序为止.比较准确的说,top命令提供了实时的对系统处理器的状态监视.它将显示系统中C ...

  9. js获取当前url地址及参数

    介绍:设置或获取对象指定的文件名或路径. window.location.pathname //返回 设置或获取整个 URL 为字符串. window.location.href 设置或获取与 URL ...

  10. 转载:IntelliJ Idea 常用快捷键列表

    IntelliJ Idea 常用快捷键列表 (http://www.open-open.com/lib/view/open1396578860887.html) Ctrl+Shift + Enter, ...