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. python 几种常见的测试框架

    1. unittest 参考文档: https://docs.python.org/3/library/unittest.html The unittest unit testing framewor ...

  2. 西安Uber优步司机奖励政策(1月11日~1月17日)

    滴快车单单2.5倍,注册地址:http://www.udache.com/ 如何注册Uber司机(全国版最新最详细注册流程)/月入2万/不用抢单:http://www.cnblogs.com/mfry ...

  3. 网络编程之TCP异步群聊:客户端代码

    最近朋友建议我写一些关于微软云技术的博客留给学校下一届的学生们看,怕下一届的MSTC断档.于是我也觉的有这个必要.写了几篇博客之后,我觉得也有必要把这一年的学习内容放在博客做个纪念,就这样写了本篇博客 ...

  4. Python开发—Ajax系列

    概述 对于WEB应用程序:用户浏览器发送请求,服务器接收并处理请求,然后返回结果,往往返回就是字符串(HTML),浏览器将字符串(HTML)渲染并显示浏览器上. 1.传统的Web应用 一个简单操作需要 ...

  5. Java Web应用的开发模式

    一.概述 从互联网诞生的第一天起,web从一个简单的静态页面,发展到今天五花八门.令人眼花缭乱的复杂应用,大到企业级的web应用系统,小到简单的SPA管理系统.在硬件性能提升的同时,通过各种技术实现了 ...

  6. redis和ehcache

    Ehcache在java项目广泛的使用.它是一个开源的.设计于提高在数据从RDBMS中取出来的高花费.高延迟采取的一种缓存方案.正因为Ehcache具有健壮性(基于java开发).被认证(具有apac ...

  7. SSH动态查询封装接口介绍

    SSH动态查询封装接口介绍 1.查询记录总条数 public int count(Class c,Object[][] eq,Object[][] like,String[] group,String ...

  8. GiB与GB

    Gibibyte(giga binary byte的缩写)是信息或计算机硬盘存储的一个单位,简称GiB.由来“GiB”.“KiB”.“MiB”等是于1999年由国际电工协会(IEC)拟定了" ...

  9. TCP的封包与拆包

    对于基于TCP开发的通讯程序,有个很重要的问题需要解决,就是封包和拆包. 一.为什么基于TCP的通讯程序需要进行封包和拆包. TCP是个"流"协议,所谓流,就是没有界限的一串数据. ...

  10. Linux 信号表

    信号 取值 默认动作 含义(发出信号的原因) SIGHUP 1 Term 终端的挂断或进程死亡 SIGINT 2 Term 来自键盘的中断信号 SIGQUIT 3 Core 来自键盘的离开信号 SIG ...