iOS监听音量变化

1,347 阅读2分钟

项目需要监听当前iOS设备音量,当音量太小时给出提示,用户调整到合适音量后去掉提示。

一、旧方法 (iOS15后失效)

原理:通过监听苹果未公开的消息 AVSystemController_SystemVolumeDidChangeNotification
(1)在程序开始时监听

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

(2)监听回调

    @objc func volumeDidChange(notification: NSNotification) {
        let volume = notification.userInfo!["AVSystemController_AudioVolumeNotificationParameter"] as! Float
        print(#line, "volumeDidChange", volume)
        // 这里加入业务代码
        
    }

(3)程序结束时记得移除监听

        NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: "AVSystemController_SystemVolumeDidChangeNotification"), object: nil)

对于iOS15, 这里还有个变体。主要将消息从AVSystemController_SystemVolumeDidChangeNotification换成SystemVolumeDidChange stackoverflow.com/questions/6…

但是iOS16试了不起作用。

二、新方法 (亲测可用,推荐)

采用苹果公开的API AVAudioSession,但需要注意的是:这个方法只在真机有效,模拟器不起作用。

(1)实现一个监听封装类VolumeListener

import Foundation
import AVFoundation

class VolumeListener: NSObject {
    private var audioSession: AVAudioSession!
    private var volumeObservation: NSKeyValueObservation?
    
    private var volumeDidChange: (_ volume:Float) -> () //音量变化通知函数

    init(volumeDidChange: @escaping (_ volume:Float) -> ()) {
        self.volumeDidChange = volumeDidChange
        super.init()
        setupAudioSession()
    }

    private func setupAudioSession() {
        audioSession = AVAudioSession.sharedInstance()

        do {
            try audioSession.setActive(true, options: [])
        } catch {
            print("Failed to activate audio session: \(error)")
            return
        }

        startObservingVolume()
    }

    private func startObservingVolume() {
        volumeObservation = audioSession.observe(\.outputVolume, options: [.initial, .new]) { [weak self] (audioSession, _) in
            let currentVolume = audioSession.outputVolume
            self?.volumeDidChange(currentVolume)
        }
    }

//    func volumeDidChange(volume: Float) {
//        print("Volume did change: \(volume)")
//        // 在此处执行音量更改后的操作
//    }

    func stopObservingVolume() {
        volumeObservation = nil
    }

    deinit {
        stopObservingVolume()
    }
}

这个函数的options参数要注意 audioSession.observe(.outputVolume, options: [.initial, .new])

以下是 NSKeyValueObservingOptions 的一些常用选项:
.new:观察者将收到属性的新值。在回调方法中,可以通过 change[.newKey] 获取新值。
.old:观察者将收到属性的旧值。在回调方法中,可以通过 change[.oldKey] 获取旧值。
.initial:在添加观察者时立即触发回调,这样可以获取属性的当前值。通常与 .new 一起使用,以便在添加观察者时收到初始值。
.prior:在属性更改之前和之后触发回调。回调将被调用两次,其中一次是在属性值改变之前。

如果options为空,则只会在音量改变时回调。如果想在用户改变音量前就获取一次当前音量,则要在options中添加.initial。

(2)在需要使用的地方 先定义

private var volumeListener: VolumeListener? //音量大小变化监听

在ViewDidLoad里初始化

volumeListener = VolumeListener(volumeDidChange:volumeDidChange(_:))

实现监听代码

    //这个在模拟器不行,只能在真机
    func volumeDidChange(_ volume: Float){
        print("volumeDidChange , volume: \(volume)")
        //业务代码
    }