如何在UIView中添加subviews.count的观察者?

时间:2017-10-06 11:49:33

标签: ios swift

我需要检测子视图的数量何时更改并执行作业。如何添加观察者并在其发生变化时获得回调?

到目前为止,我已在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:]不受支持。关键路径:计数'

2 个答案:

答案 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
        })