iOS编程修改系统音量

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;

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

时间: 2024-10-08 15:21:57

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

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

使用运行时获取系统的属性:使用自己的手势修改系统自带的手势 有的时候我需要实现一个功能,但是没有想到很好的方法或者想到了方法只是那个方法实现起来太麻烦,一或者确实为了装逼,我们就会想到iOS开发中最牛逼的技术运行时(Runtime) 关于运行时这里就不多说了,请查看笔者之前搬过来了精选文章,这里主要是怎么在实际开发中使用运行时实现我们想要的功能. 比如,在iOS开发中有这样一个问题,关于导航栏中我们点击一个按钮会跳到对应的子控制器,但是这里问题就来了,我们在对应的字控制器中可以点击对应的back

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

很简单的调用 首先在工程引入MediaPlayer.framework #import <MediaPlayer/MediaPlayer.h> 1. 获取系统音量 // 获取系统音量 MPVolumeView *volumeView = [[MPVolumeView alloc] init]; UISlider *volumeViewSlider= nil; for (UIView *view in [volumeView subviews]){ if ([view.class.descrip

IOS常用的系统文件目录介绍

iOS常用目录整理说明是本文要介绍的内容,虽然不同API全面,也算是在编程中常用到的存放目录,所以是必备文档,不多说,来看详细内容讲解. 1.[/Applications] 常用软件的安装目录 内建软体及JB软体存放位置 2. [/private /var/ mobile/Media /iphone video Recorder] 录像文件存放目录 3.[/private /var/ mobile/Media /DCIM] 相机拍摄的照片文件存放目录 4.[/private/var/ mobil

Linux01-BASH脚本编程之系统裁剪51

上接(BASH脚本编程之拷贝系统库文件50) 重新编辑grub为静默模式: 1.vim /mnt/boot/grub/grub.conf default=0 timeout=3 title Nick Linux(2.6.18) kernel /vmlinuz ro root/dev/hda2 quiet initrd /initrd.gz 如何实现终端提示信息: 2.复制宿主机文件: cp /etc/issue /mnt/sysroot/etc/ 3.重新编辑issue信息: vim /mnt/

ios编程之回调机制

ios编程之回调机制详解:   ———————————————— 函数/方法/block块一系列概念: 函数在大部分高级语言中都是个重要的概念,函数实现就是对一段代码的封装,我们一般会为了完成某一个业务功能或编程逻辑而需要组织数行代码,而这数行代码还有可能被使用多次,所以将它们封装成一个函数,每一次的执行我们称之为调函数或函数调用. 在C程序中,我们知道程序是从main函数开始执行,执行完main函数就退出程序,其实我们程序员很少去跟踪整个程序的执行流,一个程序(一段二进制代码)如何从加载到运行

Android黑科技,读取用户短信+修改系统短信数据库

安卓系统比起ios系统最大的缺点,相信大家都知道,就是系统安全问题.这篇博客就秀一波“黑科技”. 读取用户短信 Android应用能读取用户手机上的短信,相信已经不是什么新鲜事,比如我们收到的短信验证码,一些app马上就能自动获取并填上验证码,省去我们手动填写验证码.原理就是通过Android的ContentProvider组件间接访问系统的短信数据库,获取所有短信内容.下面来演示一下. 布局很简单,如下: 代码如下: public class MainActivity extends Acti

Android中通过进程注入技术修改系统返回的Mac地址

致谢 感谢看雪论坛中的这位大神,分享了这个技术:http://bbs.pediy.com/showthread.php?t=186054,从这篇文章中学习到了很多内容,如果没有这篇好文章,我在研究的过程中会遇到很多困难,说不定我就放弃了~~在此感谢他. 前言 之前的几篇文章都是在介绍了OC的相关知识,之前的半个月也都是在搞IOS的相关东西,白天上班做Android工作,晚上回家还有弄IOS,感觉真的很伤了.不过OC的知识也学习了差不多了.不过在这段时间遗留了很多Android方面的问题都没有进行

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

Delphi编程获取系统当前进程.窗口句柄.文件属性以及程序运行状态. uses TLHelp32,PsAPI; (1)显示进程列表:procedure TForm1.Button2Click(Sender: TObject);var lppe: TProcessEntry32;found : boolean;Hand : THandle;P:DWORD;s:string;beginListBox1.Items.Clear ;Hand := CreateToolhelp32Snapshot(TH

iOS编程规范(整理)

一.文档结构管理 1.建立Libraries文件夹,所有第三方库放入其中. 2.建立Utilities文件夹,自已封装的类放入其中. 3.建立Constants.h头文件,所有的常量定义于其中.Constants.h文件放入Main文件组里面. 4.每个功能块放入一个Group,在目录里建立实际文件夹管理. 5.程序资源文件放入Supporting Files文件夹中.如.plist.数据库资料等. 6.在Supporting Files文件夹下建立Image.Music和Video等相关文件夹