ios 使用KVO观察UIView的子视图数组中的变化

j9per5c4  于 2023-05-19  发布在  iOS
关注(0)|答案(4)|浏览(173)

我已经四处寻找答案,我试图实施它,但没有任何工作。基本上,我需要能够观察VC视图的子视图数组中的变化。如果从该数组中删除了一个现有视图,我希望得到有关它的通知并运行一些代码。
这可能吗?
编辑-更多信息
我试图修复一个奇怪的边缘情况错误,快速点击UISearchDisplayController的UISearchBar(非常自定义)会导致sdController(或者更确切地说是navBar效果中的托管searchBar)从视图中消失,但sdController仍然处于活动状态。这意味着navBar停留在-y原点,下面的tableView不可滚动。
我最初的想法是进入sdController处于活动状态,但UISearchDisplayControllerContainerView不在视图层次结构中的状态。我尝试在VC的viewDidLayoutSubviews中测试这个,但是,当你点击搜索栏并启动sdController动画时,sdController是活动的,而UISearchDisplayControllerContainerView不在视图层次结构中:(。

e4yzc0pl

e4yzc0pl1#

您可以观察CALayer的属性sublayers,它是KVO兼容的,而不是UIView subviews

5lhxktic

5lhxktic2#

与Apple框架中的大多数属性一样,subviewsis not KVO compliant
如果控制子视图或超视图,则可以通过子类化和覆盖来观察视图层次结构的更改:
在超级视图中你必须覆盖...

- (void)willRemoveSubview:(UIView *)subview

。。。或者,如果你控制子视图,你会覆盖。。

- (void)willMoveToSuperview

这两个方法都是在移除视图之前调用的。

myzjeezk

myzjeezk3#

Swift 3.x
使用自定义视图

class ObservableView: UIView {
    weak var delegate:ObservableViewDelegate?
    
    override func didAddSubview(_ subview: UIView) {
        super.didAddSubview(subview)
        delegate?.observableView(self, didAddSubview: subview)
    }
    
    override func willRemoveSubview(_ subview: UIView) {
        super.willRemoveSubview(subview)
        delegate?.observableview(self, willRemoveSubview: subview)
    }
    
}

protocol ObservableViewDelegate: class {
    func observableView(_ view:UIView, didAddSubview:UIView)
    func observableview(_ view:UIView, willRemoveSubview:UIView)
}

//Use

class ProfileViewController1: UIViewController, ObservableViewDelegate {
    @IBOutlet var headerview:ObervableView! //set custom class in storyboard or xib and make outlet connection
    
    override func viewDidLoad() {
        super.viewDidLoad()
        headerview.delegate = self
    }
    
    //Delegate methods
    func observableView(_ view: UIView, didAddSubview: UIView) {
        //do somthing
    }
    
    func observableview(_ view: UIView, willRemoveSubview: UIView) {
        //do somthing
    }
}
imzjd6km

imzjd6km4#

我也遇到过类似的情况。基本上,如果你需要一个视图来观察它的父子视图数组,甚至它的大小或任何对其的更改(这是不符合KVO的),以便保持它自己在顶部,例如,你可以使用关联值和一些混合的组合。
首先,声明child,并且可选地,因为我喜欢尽可能隔离这种前卫的解决方案,所以在那里嵌入一个UIView私有扩展来添加KVO机制(Swift 5.1代码):

class AlwaysOnTopView: UIView {

    private var observer: NSKeyValueObservation?
    
    override func willMove(toSuperview newSuperview: UIView?) {
        if self.superview != nil {
            self.observer?.invalidate()
        }
        super.willMove(toSuperview: newSuperview)
    }
    
    override func layoutSubviews() {
        self.superview?.bringSubviewToFront(self)
    }
    
    override func didMoveToSuperview() {
        super.didMoveToSuperview()
        
        if self.superview != nil {
            self.layer.zPosition = CGFloat.greatestFiniteMagnitude
            self.superview?.swizzleAddSubview()
            observer = self.superview?.observe(\.subviewsCount, options: .new) { (view, value) in
                guard view == self.superview else {return}
                self.superview?.bringSubviewToFront(self)
            }
        }
    }}

    private extension UIView {
        @objc dynamic var subviewsCount: Int {
            get {
                return getAssociatedValue(key: "subviewsCount", object: self, initialValue: self.subviews.count)
            }
            set {
                self.willChangeValue(for: \.subviewsCount)
                set(associatedValue: newValue, key: "subviewsCount", object: self)
                self.didChangeValue(for: \.subviewsCount)
            }
        }
        
        @objc dynamic func _swizzled_addSubview(_ view: UIView) {
            _swizzled_addSubview(view)
            self.subviewsCount = self.subviews.count
        }
        
        func swizzleAddSubview() {
            let selector1 = #selector(UIView.addSubview(_:))
            let selector2 = #selector(UIView._swizzled_addSubview(_:))
            
            let originalMethod = class_getInstanceMethod(UIView.self, selector1)!
            let swizzleMethod = class_getInstanceMethod(UIView.self, selector2)!
            method_exchangeImplementations(originalMethod, swizzleMethod)
        }
    }
}

这样,您可以保持内部属性可观察,并与任何添加的视图保持一致。这只是一个快速的实现,有许多角落的情况下处理(例如:使用insert或任何其他UIView方法添加的视图等),但这只是一个起点。此外,这可以根据不同的需求进行调整(例如,观察兄弟姐妹,而不仅仅是父母,等等)。

相关问题