KVC 方法: keyPathsForValuesAffectingValueForKey: 在 CIFilter中的应用
/// KVC, 返回能触发对应输入参数 key 的 其他key的集合。
override class func keyPathsForValuesAffectingValue(forKey key: String) -> Set<String> {
// 根据测试发现 super.keyPathsForValuesAffectingValue 会收集到 input*** 形式的参数
var supSet = super.keyPathsForValuesAffectingValue(forKey: key)
// KVC 机制,如果设置 "toneMap", 会出发监听 ""outputImage""
if key == "outputImage" {
supSet.insert("toneMap")
}
return supSet
}
先说结论:
- 前提是key(本例是 outputImage)要与其他的key绑定(进行KVC绑定)
- 返回能触发对应输入参数 key 的 其他key的集合。
- 根据测试发现 super.keyPathsForValuesAffectingValue 会收集到 input*** 形式的参数
例如上面的方法,当设置toneMap的值时,会出发filter的outputImage方法。
如果 返回的Set中不包含 “toneMap”, 则设置toneMap时不会触发outputImage
测试步骤:
- 创建TestFilter
import Cocoa
class TestFilter: CIFilter {
@objc dynamic var inputImage: CIImage?
@objc dynamic var inputWidth: Double = 0
@objc dynamic var toneMap: Double = 0
@objc dynamic var inputToneMap: Double = 0
var inputToneMap2: Double = 0
/// KVC, 返回能触发对应输入参数 key 的 其他key的集合。
override class func keyPathsForValuesAffectingValue(forKey key: String) -> Set<String> {
// 根据测试发现 super.keyPathsForValuesAffectingValue 会收集到 input*** 形式的参数
var supSet = super.keyPathsForValuesAffectingValue(forKey: key)
// KVC 机制,如果设置 "inputWidth", 会出发监听 ""outputImage""
if key == "outputImage" {
supSet.insert("toneMap")
}
return supSet
}
override var attributes: [String: Any] {
return [
kCIAttributeFilterDisplayName: "TestFilter", // filter name
"inputImage": [kCIAttributeIdentity : 0,
kCIAttributeClass : "CIImage",
kCIAttributeDisplayName : "Image",
kCIAttributeType : kCIAttributeTypeImage],
"inputWidth" : [kCIAttributeIdentity : 0,
kCIAttributeClass : "NSNumber",
kCIAttributeDefault : 0,
kCIAttributeDisplayName : "inputWidth",
kCIAttributeType : kCIAttributeTypeScalar],
"toneMap" : [kCIAttributeIdentity : 0,
kCIAttributeClass : "NSNumber",
kCIAttributeDefault : 0,
kCIAttributeDisplayName : "toneMap",
kCIAttributeType : kCIAttributeTypeScalar],
"inputToneMap" : [kCIAttributeIdentity : 0,
kCIAttributeClass : "NSNumber",
kCIAttributeDefault : 0,
kCIAttributeDisplayName : "inputToneMap",
kCIAttributeType : kCIAttributeTypeScalar],
"inputToneMap2" : [kCIAttributeIdentity : 0,
kCIAttributeClass : "NSNumber",
kCIAttributeDefault : 0,
kCIAttributeDisplayName : "inputToneMap2",
kCIAttributeType : kCIAttributeTypeScalar]]
}
override var outputImage: CIImage? {
let result = inputImage?.transformed(by: .init(scaleX: 0.2, y: 0.2))
return result
}
}
- 创建TestFilter2, 用于将TestFilter的 ’outputImage‘ 与TestFilter2 的 ’inputImage‘ 绑定
import Cocoa
class TestFilter2: CIFilter {
@objc dynamic var inputImage: CIImage?
override var attributes: [String: Any] {
return [
kCIAttributeFilterDisplayName: "TestFilter2", // filter name
"inputImage": [kCIAttributeIdentity : 0,
kCIAttributeClass : "CIImage",
kCIAttributeDisplayName : "Image",
kCIAttributeType : kCIAttributeTypeImage]]
}
override var outputImage: CIImage? {
let result = inputImage?.transformed(by: .init(scaleX: 0.2, y: 0.2))
return result
}
}
- 绑定,并测试
func testFilterChain() {
let testF1 = TestFilter.init()
let testF2 = TestFilter2.init()
// 将 TestFilter2 的 ”inputImage“ 与 TestFilter 的 ”outputImage“绑定
testF2.bind(NSBindingName(rawValue: "inputImage"), to: testF1, withKeyPath: kCIOutputImageKey, options: nil)
// 测试,设置值时会出发 TestFilter 的 outputImage
testF1.inputWidth = 100.0
testF1.setValue(19.0, forKey: "inputWidth")
testF1.toneMap = 0.2
let t1OutImg = testF1.outputImage
}