Unity开发中如何实现iOS(iPhone)设备系统音量调节
Unity 实现iOS端系统音量调节方案
首先明确iOS平台的规则限制:苹果没有开放直接修改系统全局音量的公开API,你在安卓端通过AudioManager.setStreamVolume直接修改音量的逻辑无法直接照搬,所有通过私有API强行修改系统音量的实现都会在App Store审核阶段被拒。
合规可过审的实现方案是使用苹果官方提供的MPVolumeView组件,最终实现的效果和你现有安卓代码完全一致:支持获取/设置0~1区间的媒体音量(对应安卓的STREAM_MUSIC流),调节时可弹出系统原生音量提示UI。
具体实现步骤
- 补全C#侧跨平台调用逻辑,通过条件编译区分安卓、iOS平台实现
- 编写iOS原生插件,封装
MPVolumeView的音量读写逻辑,放到Unity指定的插件目录下
1. 补全后的C#跨平台音量控制代码
直接替换你原有的SmartphoneVolumeController类即可,原有安卓逻辑完全保留,新增iOS平台调用逻辑:
using System.Collections; using System.Collections.Generic; using UnityEngine; #if UNITY_IOS using System.Runtime.InteropServices; #endif public class SmartphoneVolumeController : MonoBehaviour { public static SmartphoneVolumeController Instance; private AndroidNativeVolumeService androidService; private void Awake() { if (Instance == null) Instance = this; else Destroy(gameObject); } #if UNITY_IOS [DllImport("__Internal")] private static extern float iOS_GetSystemVolume(); [DllImport("__Internal")] private static extern void iOS_SetSystemVolume(float volume); #endif /// <summary> /// 获取当前系统媒体音量,返回值范围0~1 /// </summary> public float GetSystemVolume() { #if UNITY_ANDROID if (androidService == null) androidService = new AndroidNativeVolumeService(); return androidService.GetSystemVolume(); #elif UNITY_IOS return iOS_GetSystemVolume(); #else return 1f; #endif } /// <summary> /// 设置系统媒体音量,传入值范围0~1 /// </summary> public void SetSystemVolume(float volumeValue) { volumeValue = Mathf.Clamp01(volumeValue); #if UNITY_ANDROID if (androidService == null) androidService = new AndroidNativeVolumeService(); androidService.SetSystemVolume(volumeValue); #elif UNITY_IOS iOS_SetSystemVolume(volumeValue); #endif } // 原有安卓音量服务实现保持不变 public class AndroidNativeVolumeService { static int STREAMMUSIC; static int FLAGSHOWUI = 1; private static AndroidJavaObject audioManager; private static AndroidJavaObject deviceAudio { get { if (audioManager == null) { AndroidJavaClass unityPlayerClass = new AndroidJavaClass("com.unity3d.player.UnityPlayer"); AndroidJavaObject currentActivity = unityPlayerClass.GetStatic<AndroidJavaObject>("currentActivity"); AndroidJavaObject context = currentActivity.Call<AndroidJavaObject>("getApplicationContext"); AndroidJavaClass audioManagerClass = new AndroidJavaClass("android.media.AudioManager"); AndroidJavaClass contextClass = new AndroidJavaClass("android.content.Context"); STREAMMUSIC = audioManagerClass.GetStatic<int>("STREAM_MUSIC"); string Context_AUDIO_SERVICE = contextClass.GetStatic<string>("AUDIO_SERVICE"); audioManager = context.Call<AndroidJavaObject>("getSystemService", Context_AUDIO_SERVICE); if (audioManager != null) Debug.Log("[AndroidNativeVolumeService] Android Audio Manager successfully set up"); else Debug.Log("[AndroidNativeVolumeService] Could not read Audio Manager"); } return audioManager; } } private static int GetDeviceMaxVolume() { return deviceAudio.Call<int>("getStreamMaxVolume", STREAMMUSIC); } public float GetSystemVolume() { int deviceVolume = deviceAudio.Call<int>("getStreamVolume", STREAMMUSIC); float scaledVolume = (float)(deviceVolume / (float)GetDeviceMaxVolume()); return scaledVolume; } public void SetSystemVolume(float volumeValue) { int scaledVolume = (int)(volumeValue * (float)GetDeviceMaxVolume()); deviceAudio.Call("setStreamVolume", STREAMMUSIC, scaledVolume, FLAGSHOWUI); } } }
2. iOS原生插件代码
新建名为iOSVolumeController.mm的文件,放到Unity项目的Assets/Plugins/iOS目录下,代码内容如下:
#import <Foundation/Foundation.h> #import <MediaPlayer/MediaPlayer.h> #import <AVFoundation/AVFoundation.h> #import <UIKit/UIKit.h> static MPVolumeView *volumeView = nil; static UISlider *volumeSlider = nil; void SetupVolumeView() { if (volumeView != nil) return; // 初始化音频会话,保证音量控制在应用前后台都正常生效 [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil]; [[AVAudioSession sharedInstance] setActive:YES error:nil]; // 若不需要显示系统音量弹窗,将frame改为CGRectMake(-100, -100, 10, 10)放到屏幕外即可 volumeView = [[MPVolumeView alloc] initWithFrame:CGRectMake(0, [UIScreen mainScreen].bounds.size.height - 10, [UIScreen mainScreen].bounds.size.width, 10)]; volumeView.showsVolumeSlider = YES; [[[UIApplication sharedApplication] keyWindow] addSubview:volumeView]; // 查找MPVolumeView内置的音量滑块控件 for (UIView *view in volumeView.subviews) { if ([view isKindOfClass:[UISlider class]]) { volumeSlider = (UISlider *)view; break; } } } extern "C" float iOS_GetSystemVolume() { SetupVolumeView(); if (volumeSlider == nil) return 1.0f; return volumeSlider.value; } extern "C" void iOS_SetSystemVolume(float volume) { SetupVolumeView(); if (volumeSlider == nil) return; volume = fmaxf(0.0f, fminf(1.0f, volume)); [volumeSlider setValue:volume animated:NO]; // 触发滑块值变更事件,生效系统音量修改 [volumeSlider sendActionsForControlEvents:UIControlEventTouchUpInside]; }
注意事项
- 该方案使用苹果官方公开API实现,不会触发App Store审核拒审,接口返回/传入的音量值和安卓端保持一致,都是0~1的浮点数,上层调用不需要做额外适配
- 不要尝试直接修改
AVAudioSession的outputVolume属性,该属性为只读,强行调用私有API修改会直接被审核打回 - 打包Xcode工程时,Unity会自动引入MediaPlayer、AVFoundation、UIKit等依赖框架,不需要手动在Xcode中额外配置
- 若应用切换场景后音量滑块失效,可在场景加载完成后重新调用一次
SetupVolumeView逻辑(也可将MPVolumeView实例绑定到不销毁的根节点上保证全局存活)
内容的提问来源于stack exchange,提问作者sayt
相关产品推荐
相关产品推荐

