I have a UIViewController with the following code. I want to know when the value of portrait effect is changed (in control center). I have tried AVCaptureDevice.isPortraitEffectEnabled
and .portraitEffectEnabled
, both have the same result: observeValue()
is never called. I have verified that the value itself does actually change, and the docs state that KVO is supported for this member.
What am I missing?
To test this I am toggling the value of portaitEffectEnabled
by calling AVCaptureDevice.showSystemUserInterface(.videoEffects)
and turning it on/off, and expecting the KVO to fire.
@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)
}
}
CodePudding user response:
That won’t work because the AVCaptureDevice
class itself doesn’t have a portraitEffectSupported
property.
The issue is that the portraitEffectSupported
property is an instance property.
you can always use class_copyPropertyList
to double check that the property you’re trying to observe actually exists on that object. Here's an example:
import AVFoundation
func getPropertyNames(of target: AnyObject) -> [String] {
let itsClass: AnyClass = object_getClass(target)!
var count = UInt32()
guard let p = class_copyPropertyList(itsClass, &count) else {
return []
}
defer { p.deallocate() }
let properties = UnsafeBufferPointer(start: p, count: Int(count))
return properties.map { String(cString: property_getName($0)) }
}
// `AVCaptureDevice` has no class properties.
let propertiesOfTheClassItself = getPropertyNames(of: AVCaptureDevice.self)
print(propertiesOfTheClassItself) // => []
// Instances of `AVCaptureDevice` have some instance properties.
let propertiesOfASampleInstance = getPropertyNames(of: AVCaptureDevice.default(for: .video)!)
print(propertiesOfASampleInstance) // => ["transportControlsSupported", "transportControlsPlaybackMode", "transportControlsSpeed", "adjustingFocus", "adjustingExposure", "adjustingWhiteBalance"]