我需要检测子视图的数量何时更改并执行作业。如何添加观察者并在其发生变化时获得回调?
到目前为止,我已在AppDelegate
内尝试过:
private func setupObserver() {
window?.addObserver(self, forKeyPath: "subviews.count", options: NSKeyValueObservingOptions.new, context: nil)
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
print(keyPath)
}
但它崩溃了:
由于未捕获的异常'NSInvalidArgumentException'而终止应用程序,原因:'[< __ NSArrayM 0x6000002520f0> addObserver:forKeyPath:options:context:]不受支持。关键路径:计数'
答案 0 :(得分:1)
除KVO之外的另一种解决方案
您可以将UIWindow
子类化为
class MyWindow: UIWindow {
override func didAddSubview(_ subview: UIView) {
print("Subview added")
}
}
然后在AppDelegate
中,您可以覆盖window
属性,如下所示
var _window: MyWindow?
var window: UIWindow? {
get {
_window = _window ?? MyWindow(frame: UIScreen.main.bounds)
return _window
}
set { }
}
现在每当添加新对象时,都会调用被覆盖的方法didAddSubview
。您可以根据需要覆盖更多方法。
didAddSubview(:) , willRemoveSubview( :), willMove(toSuperview:), didMoveToSuperview()
window
属性的摘录
此合成属性的默认值为nil,这会导致应用程序创建通用UIWindow对象并将其分配给属性。如果要为应用程序提供自定义窗口,则必须实现此属性的getter方法,并使用它创建并返回自定义窗口。
答案 1 :(得分:0)
怎么样
import UIKit
typealias ObserveHandler = (UIView) -> Void
var keyUIViewObservingHandler: UInt8 = 0xe
extension UIView{
func observingRemoveFromSuperview(){
let parent = self.findObserverParent()
self.observingRemoveFromSuperview()
self.notifyObservingParent(self, parent: parent)
}
func onDidAddSubview(_ subview: UIView){
self.notifyObservingParent(subview)
}
private func notifyObservingParent(_ subview: UIView, parent: UIView? = nil){
if let observingParent = parent ?? self.findObserverParent(){
observingParent.getObservingHandler()?(subview)
}
}
private func findObserverParent() -> UIView?{
return self.superview?.findObserverParentPvt()
}
private func findObserverParentPvt() -> UIView?{
if self.isObservingHierarchy(){
return self
}else{
return self.superview?.findObserverParentPvt()
}
}
private func isObservingHierarchy() -> Bool{
return objc_getAssociatedObject(self, &keyUIViewObservingHandler) != nil
}
func observeHiearachy(handler: ObserveHandler){
objc_setAssociatedObject(self, &keyUIViewObservingHandler, handler, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN)
}
private func getObservingHandler() -> ObserveHandler?{
return objc_getAssociatedObject(self, &keyUIViewObservingHandler) as? ObserveHandler
}
}
确保在AppDelegate中调用这些方法:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
self.setupViewHierarchyObserving()
return true
}
func setupViewHierarchyObserving(){
let originalSelector = #selector(UIView.didAddSubview(_:))
let swizzledSelector = #selector(UIView.onDidAddSubview(_:))
let viewClass = UIView.self
let originalMethod = class_getInstanceMethod(viewClass, originalSelector)
let swizzledMethod = class_getInstanceMethod(viewClass, swizzledSelector)
let didAddMethod = class_addMethod(viewClass, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod))
if didAddMethod {
class_replaceMethod(viewClass, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod))
} else {
method_exchangeImplementations(originalMethod, swizzledMethod)
}
let originalSelector2 = #selector(UIView.removeFromSuperview)
let swizzledSelector2 = #selector(UIView.observingRemoveFromSuperview)
let originalMethod2 = class_getInstanceMethod(viewClass, originalSelector2)
let swizzledMethod2 = class_getInstanceMethod(viewClass, swizzledSelector2)
let didAddMethod2 = class_addMethod(viewClass, originalSelector2, method_getImplementation(swizzledMethod2), method_getTypeEncoding(swizzledMethod2))
if didAddMethod2 {
class_replaceMethod(viewClass, swizzledSelector2, method_getImplementation(originalMethod2), method_getTypeEncoding(originalMethod2))
} else {
method_exchangeImplementations(originalMethod2, swizzledMethod2)
}
}
现在您可以观察视图层次结构,简单如下:
self.view.observeHiearachy(handler: { (subview) in
// your code here
})