如何让Swift KVO对AVCaptureDevice静态成员portraitEffectEnabled生效?
问题:监听AVCaptureDevice的portraitEffectEnabled变化时KVO未触发
我有一个UIViewController,代码如下。希望监听控制中心内人像效果(portrait effect)值的变化,尝试了AVCaptureDevice.isPortraitEffectEnabled和.portraitEffectEnabled两种方式,但都遇到相同问题:observeValue()从未被调用。我已确认该值确实会变化,且文档表明该属性支持KVO。
测试时,我通过调用AVCaptureDevice.showSystemUserInterface(.videoEffects)来切换portraitEffectEnabled的开关,期望KVO能触发。
@objc class EventSettingsCaptureViewController : UIViewController, ... { required init(...) { super.init(nibName: nil, bundle: nil) if #available(iOS 15.0, *) { AVCaptureDevice.self.addObserver(self, forKeyPath: "portraitEffectEnabled", options: [.new], context: nil) } } deinit { if #available(iOS 15.0, *) { AVCaptureDevice.self.removeObserver(self, forKeyPath: "portraitEffectEnabled", context: nil) } } override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { // Breakpoint set here: never hits if keyPath == "portraitEffectEnabled" { guard let object = object as? AVCaptureDevice.Type else { return } if #available(iOS 15.0, *) { WLog("isPortraitEffectEnabled changed: \(object.isPortraitEffectEnabled)") } } else { super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context) } } }
问题原因与解决方案
问题核心在于:你监听的是AVCaptureDevice类本身,但portraitEffectEnabled的KVO通知是发送给具体的AVCaptureDevice实例的,类对象不会触发这个属性的变更通知。
正确的做法是获取实际的视频捕获设备实例(比如前置摄像头,人像效果通常用于前置),然后给这个实例添加KVO监听,具体步骤如下:
- 获取当前用于视频捕获的设备实例(以前置摄像头为例)
- 给该实例添加KVO监听,而非
AVCaptureDevice.self - 在deinit中移除该实例的监听
修改后的代码示例:
@objc class EventSettingsCaptureViewController : UIViewController { private var videoDevice: AVCaptureDevice? required init(...) { super.init(nibName: nil, bundle: nil) if #available(iOS 15.0, *) { // 获取前置摄像头设备 videoDevice = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .front) // 给设备实例添加监听 videoDevice?.addObserver(self, forKeyPath: "portraitEffectEnabled", options: [.new], context: nil) } } deinit { if #available(iOS 15.0, *) { // 移除设备实例的监听 videoDevice?.removeObserver(self, forKeyPath: "portraitEffectEnabled", context: nil) } } override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { if keyPath == "portraitEffectEnabled" { guard let device = object as? AVCaptureDevice else { return } if #available(iOS 15.0, *) { WLog("portraitEffectEnabled changed: \(device.portraitEffectEnabled)") } } else { super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context) } } }
额外注意事项
- 确保获取到的
videoDevice不为nil,否则监听不会生效 - 如果场景中存在设备切换(比如前后摄像头切换),需要在切换前移除旧设备的监听,再给新设备添加监听
内容的提问来源于stack exchange,提问作者xaphod
相关产品推荐
相关产品推荐

