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

[英]How to add observer for subviews.count in UIView?


I need to detect when number of subviews changes and perform a job then. How to add an observer and get a callback when it changes?

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

So far I have tried within AppDelegate:

到目前为止,我已经在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)
}

but it crashes:

但它崩溃了:

Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '[<__NSArrayM 0x6000002520f0> addObserver:forKeyPath:options:context:] is not supported. Key path: count'

由于未捕获的异常'NSInvalidArgumentException'而终止应用程序,原因:'[<__ NSArrayM 0x6000002520f0> addObserver:forKeyPath:options:context:]不受支持。关键路径:计数'

2 个解决方案

#1


1  

Another solution apart from the KVO

除KVO之外的另一种解决方案

You can subclass UIWindow as below

你可以将UIWindow子类化如下

class MyWindow: UIWindow {
  override func didAddSubview(_ subview: UIView) {
    print("Subview added")
  }
}

Then in AppDelegate you can override the window property as below

然后在AppDelegate中,您可以覆盖窗口属性,如下所示

var _window: MyWindow?
var window: UIWindow? {
  get {
    _window = _window ?? MyWindow(frame: UIScreen.main.bounds)
    return _window
  }
  set { }
}

Now whenever a new object is added your overridden method didAddSubview will be called. You can override more methods according to your need.

现在,只要添加了新对象,就会调用重写方法didAddSubview。您可以根据需要覆盖更多方法。

didAddSubview(:), willRemoveSubview(:), willMove(toSuperview:), didMoveToSuperview()

didAddSubview(:),willRemoveSubview(:),willMove(toSuperview :),didMoveToSuperview()

Excerpt of window property

窗口属性的摘录

The default value of this synthesized property is nil, which causes the app to create a generic UIWindow object and assign it to the property. If you want to provide a custom window for your app, you must implement the getter method of this property and use it to create and return your custom window.

此合成属性的默认值为nil,这会导致应用程序创建通用UIWindow对象并将其分配给属性。如果要为应用程序提供自定义窗口,则必须实现此属性的getter方法,并使用它创建并返回自定义窗口。

#2


0  

How about

怎么样

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
    }

}

Make sure to swizzle these methods in the AppDelegate:

确保在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)
        }
    }

Now you can observe the view hierarchy as simple as:

现在,您可以观察视图层次结构,如下所示:

self.view.observeHiearachy(handler: { (subview) in
     // your code here
        })
智能推荐

注意!

本站翻译的文章,版权归属于本站,未经许可禁止转摘,转摘请注明本文地址:http://www.itdaan.com/blog/2017/10/06/7205ff7118933eea526ac4da2e07c362.html



 
© 2014-2019 ITdaan.com 粤ICP备14056181号  

赞助商广告