系统卷更改观察器在iOS 15上不工作

kx5bkwkv  于 2022-09-19  发布在  iOS
关注(0)|答案(6)|浏览(411)

我使用了以下代码来检测用户更改的系统音量。

NotificationCenter.default.addObserver(self, selector: #selector(volumeChanged), name: NSNotification.Name(rawValue: "AVSystemController_SystemVolumeDidChangeNotification"), object: nil)

当我更新到iOS 15时,我发现这个代码不起作用,但在任何以前的iOS版本上都能起作用。

我还使用了addObserver函数,但这没问题。

这是iOS 15的错误吗?如果是,我可以做些什么来修复它。

谢谢:)

py49o6xq

py49o6xq1#

我挂接了MPVolumeControllerSystemDataSource的方法**_systemVolumeDidChange**,在iOS 15.0(至少Beta2)通知名称改为SystemVolumeDidChange,新的通知结构如下:

{
    AudioCategory = "Audio/Video";
    Reason = ExplicitVolumeChange;
    SequenceNumber = 1069;
    Volume = 0;
}

有两点需要注意:

1.iOS 15(至少Beta2)的这条通知,即使按一次音量键也会被调用两次,但SequenceNumber是相等的;
1.该通知回调不在主线程上。

p8ekf7hl

p8ekf7hl2#

您正在执行的操作不受支持,因此,如果它不能在所有系统上运行,也就不足为奇了。有文档记录的正确方法是在音频会话outputVolume属性上使用KVO:https://developer.apple.com/documentation/avfaudio/avaudiosession/1616533-outputvolume

3j86kqsm

3j86kqsm3#

尝试了AdamWang的答案后,我发现您需要创建并保留一个MPVolumeView示例(但不需要添加到您的视图层次结构中),否则通知将不会发出。

h43kikqp

h43kikqp4#

如果有人突然不明白如何应用AdamWang的解决方案,您只需将“AVSystemController_SystemVolumeDidChangeNotification”替换为“SystemVolumeDidChange”。

xxhby3vn

xxhby3vn5#

在iOS15中,不再调用@“AVSystemController_SystemVolumeDidChangeNotification”通知。

取而代之的是关键值观察。(在上面马特回答的基础上进行扩展)

在您的ViewController.m文件中


# import <AVFoundation/AVFoundation.h>

# import <MediaPlayer/MediaPlayer.h>

@interface ViewController : UIViewController
{
    AVAudioSession *audioSession;
}

@end

在您的View Controller.m文件中

-(void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];

    audioSession = [AVAudioSession sharedInstance];
    [audioSession setActive:YES error:nil];
    [audioSession addObserver:self forKeyPath:@"outputVolume" options:0 context:nil];

}

- (void)viewWillDisappear:(BOOL)animated
{
    [super viewWillDisappear:animated]; 

    [audioSession removeObserver:self forKeyPath:@"outputVolume"];
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {

    CGFloat newVolume = audioSession.outputVolume;
    NSLog(@"newVolume: %f", newVolume);

      //if the volume gets to max or min observer won't trigger
    if (newVolume > 0.9 || newVolume < 0.1) {
        [self setSystemVolume:0.5];
        return;
    }
}

  //set the volume programatically
- (void)setSystemVolume:(CGFloat)volume {
    #pragma clang diagnostic push
    #pragma clang diagnostic ignored "-Wdeprecated-declarations"
    [[MPMusicPlayerController applicationMusicPlayer] setVolume:(float)volume];
    #pragma clang diagnostic pop
}

您可以使用移出屏幕的MPVolumeView隐藏音量滑块。

Hide device Volume HUD view while adjusitng volume with MPVolumeView slider

pvcm50d1

pvcm50d16#

我也在努力解决处理音量按钮按下事件的任务,目标是

  • 获得活动
  • 识别音量是调高还是调低
  • 即使在系统最大/最小级别也能做出React
  • 处理事件(在我的例子中,执行WKWebView中的js回调)
  • 使其在iOS 15/iOS 15以下版本上均可运行。

适用于我的最终解决方案(根据2022年9月)如下:

在我的视图控制器中

var lastVolumeNotificationSequenceNumber: Int? //see below explanations - avoiding duplicate events
var currentVolume: Float? //needed to remember your current volume, to properly react on up/down events

在我的加载查看函数中:

if #available(iOS 15, *) {
            NotificationCenter.default.addObserver(self, selector: #selector(volumeChanged(_:)), name: NSNotification.Name(rawValue: "SystemVolumeDidChange"), object: nil)
        }
        else {
            NotificationCenter.default.addObserver(self, selector: #selector(volumeChanged(_:)), name: NSNotification.Name(rawValue: "AVSystemController_SystemVolumeDidChangeNotification"), object: nil)
        }

可用标签允许您选择根据iOS版本设置的通知。

...我的视图控制器有这个:

@objc func volumeChanged(_ notification: NSNotification) {
        DispatchQueue.main.async { [self] in
            if #available(iOS 15, *) {
                volumeControlIOS15(notification)
            }
            else {
                volumeControlIOS14(notification)
            }
        }
    }

这是为了处理事件本身并区分14/15版本的代码(略有不同)

请注意:这里使用的DispatchQueue.main.async,只要完成处理程序(如上所述)不在主线程上,而在我的例子中,它必须在主线程上。在我弄清楚这一点之前,我遇到了一些崩溃和线程警告。

func manageVolume(volume: Float, minVolume: Float) {
        switch volume {
        case minVolume: do {
            currentVolume = minVolume + 0.0625
        }
        case 1: do {
            currentVolume = 0.9375
        }
        default: break
        }

        if let cV = currentVolume {
            if volume > cV {
                //do your stuff here
            }
            if volume < cV {
                //do your stuff here
            }
            currentVolume = volume
        }
        else {
            currentVolume = volume
        }
    }

此函数用于处理音量按钮按下事件,并帮助您a)了解事件是“向上”还是“向下”,以及b)管理达到最大/最小值的情况,如果您在触摸最大/最小值时仍需要继续事件处理(这是通过简单地减少/增加当前音量变量来完成的-记住,您不能改变系统音量本身,但是,变量是您的;)

func volumeControlIOS15(_ notification: NSNotification) {
        let minVolume: Float = 0.0625

        if let volume = notification.userInfo!["Volume"] as? Float {
            //avoiding duplicate events if same ID notification was generated
            if let seqN = self.lastVolumeNotificationSequenceNumber {
                if seqN == notification.userInfo!["SequenceNumber"] as! Int {
                    NSLog("Duplicate nofification received")
                }
                else {
                    self.lastVolumeNotificationSequenceNumber = (notification.userInfo!["SequenceNumber"] as! Int)
                    manageVolume(volume: volume, minVolume: minVolume)
                }
            }
            else {
                self.lastVolumeNotificationSequenceNumber = (notification.userInfo!["SequenceNumber"] as! Int)
                manageVolume(volume: volume, minVolume: minVolume)
            }
        }
    }

它是主要的iOS 15实施功能。正如你所看到的,MinVolume不是一个数字,它是一个let常量--它与iOS14不同(我在iOS14上发现它是0,而iOS15在我的物理设备上不会低于0.0625--请不要问我为什么,这是一个谜;)

它还处理最后一个通知唯一ID并省略重复的通知事件,这在iOS15中(以某种方式)相当常见。

func volumeControlIOS14(_ notification: NSNotification) {
        //old implementation for iOS < 15
        let minVolume: Float = 0

        if let volume = notification.userInfo!["AVSystemController_AudioVolumeNotificationParameter"] as? Float {
            manageVolume(volume: volume, minVolume: minVolume)
        }

    }

IOS 14的情况与此相同,主要区别有3点:a)如上所述,通知UserInfo键不同;b)没有重复的通知控制--只要我在iOS 14上没有观察到重复的通知;c)minVolume为0,这对于iOS 14是正确的

希望对您有所帮助:)

相关问题