You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unity桥接Swift时iOS Notification Center未触发音量变化通知问题

解决iOS端Unity应用音量按键检测无响应的问题

我明白你现在遇到的困境:用Objective-C++桥接Swift对接原生iOS代码,想通过订阅AVSystemController的通知来检测音量按键,但通知始终没触发对吧?我帮你梳理几个关键问题点和可行的修复方案:

1. 先排查AVSystemController通知的注册问题

首先,AVSystemController的通知注册有几个容易踩坑的细节,比如通知名称的准确性、注册时机是否在主线程,还有参数的正确获取方式。你可以试试修正你的VolumeDetector.mm代码:

#import <Foundation/Foundation.h>
#import <AVFoundation/AVFoundation.h>

@interface VolumeDetector ()
@property (nonatomic, strong) id volumeNotificationObserver;
@end

@implementation VolumeDetector

+ (void)setupVolumeDetection {
    // iOS通知中心操作必须在主线程执行
    dispatch_async(dispatch_get_main_queue(), ^{
        NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
        // 注意这个通知名称是AVSystemController的标准音量变化通知
        NSString *volumeChangeNote = @"AVSystemController_SystemVolumeDidChangeNotification";
        
        __weak typeof(self) weakSelf = self;
        self.volumeNotificationObserver = [nc addObserverForName:volumeChangeNote
                                                           object:nil
                                                            queue:[NSOperationQueue mainQueue]
                                                       usingBlock:^(NSNotification *note) {
            NSDictionary *userInfo = note.userInfo;
            // 获取音量值和变化原因
            float currentVolume = [[userInfo objectForKey:@"AVSystemController_AudioVolumeNotificationParameter"] floatValue];
            NSString *changeReason = [userInfo objectForKey:@"AVSystemController_AudioVolumeChangeReasonNotificationParameter"];
            
            // 只响应物理按键触发的音量变化(排除系统自动调整的情况)
            if ([changeReason isEqualToString:@"ExplicitVolumeChange"]) {
                // 把事件传递给Unity的C#层,这里的"VolumeManager"是你Unity中挂载脚本的GameObject名称
                UnitySendMessage("VolumeManager", "OnVolumeKeyPressed", [NSString stringWithFormat:@"%.1f", currentVolume].UTF8String);
            }
        }];
    });
}

+ (void)cleanupVolumeDetection {
    dispatch_async(dispatch_get_main_queue(), ^{
        if (self.volumeNotificationObserver) {
            [[NSNotificationCenter defaultCenter] removeObserver:self.volumeNotificationObserver];
            self.volumeNotificationObserver = nil;
        }
    });
}

@end

2. 更稳妥的合规方案:用公开API替代AVSystemController

要注意,AVSystemController的部分通知属于半私有API,上架App Store可能会有审核风险。推荐用完全合规的AVAudioSession监听方案,代码如下:

#import <Foundation/Foundation.h>
#import <AVFoundation/AVFoundation.h>

@interface VolumeDetector () <AVAudioSessionDelegate>
@property (nonatomic, strong) AVAudioSession *audioSession;
@property (nonatomic, assign) float lastRecordedVolume;
@end

@implementation VolumeDetector

+ (void)setupVolumeDetection {
    dispatch_async(dispatch_get_main_queue(), ^{
        AVAudioSession *session = [AVAudioSession sharedInstance];
        NSError *activateError = nil;
        // 激活音频会话才能监听音量变化
        [session setActive:YES error:&activateError];
        if (activateError) {
            NSLog(@"激活音频会话失败:%@", activateError.localizedDescription);
            return;
        }
        [session setDelegate:self];
        self.audioSession = session;
        self.lastRecordedVolume = session.outputVolume;
    });
}

- (void)audioSessionOutputVolumeDidChange:(NSNotification *)notification {
    float currentVolume = self.audioSession.outputVolume;
    // 判断音量变化方向,区分+/-按键
    if (currentVolume > self.lastRecordedVolume) {
        UnitySendMessage("VolumeManager", "OnVolumeUpPressed", [NSString stringWithFormat:@"%.1f", currentVolume].UTF8String);
    } else if (currentVolume < self.lastRecordedVolume) {
        UnitySendMessage("VolumeManager", "OnVolumeDownPressed", [NSString stringWithFormat:@"%.1f", currentVolume].UTF8String);
    }
    self.lastRecordedVolume = currentVolume;
}

+ (void)cleanupVolumeDetection {
    dispatch_async(dispatch_get_main_queue(), ^{
        [self.audioSession setDelegate:nil];
        [self.audioSession setActive:NO error:nil];
        self.audioSession = nil;
    });
}

@end

3. Unity端C#接收代码

在Unity中创建一个名为VolumeManager的脚本,挂载到任意活跃的GameObject上,用来接收原生层传递的事件:

using UnityEngine;

public class VolumeManager : MonoBehaviour
{
    // 对应原生层传递的通用音量按键事件
    public void OnVolumeKeyPressed(string volumeString)
    {
        if (float.TryParse(volumeString, out float volume))
        {
            Debug.Log($"音量按键被触发,当前音量:{volume}");
            // 这里添加你的业务逻辑处理
        }
    }

    // 对应音量+按键事件
    public void OnVolumeUpPressed(string volumeString)
    {
        if (float.TryParse(volumeString, out float volume))
        {
            Debug.Log($"音量+键被按下,当前音量:{volume}");
        }
    }

    // 对应音量-按键事件
    public void OnVolumeDownPressed(string volumeString)
    {
        if (float.TryParse(volumeString, out float volume))
        {
            Debug.Log($"音量-键被按下,当前音量:{volume}");
        }
    }
}

4. 关键注意事项

  • 注册时机:要在Unity应用启动后(比如Start方法里)调用原生的setupVolumeDetection方法,不要过早注册。
  • 权限配置:iOS 10+需要在Info.plist中添加NSMicrophoneUsageDescription权限描述(即使不需要麦克风,监听音量也可能需要这个权限)。
  • 资源清理:在应用退出或场景销毁时,记得调用cleanupVolumeDetection移除监听,避免内存泄漏。

内容的提问来源于stack exchange,提问作者bluey31

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 07:56:39