Well, I have stumbled upon a similar situation. Basically, if you need a view to observe its parent subviews array, or even its size or any change to it (which is non KVO compliant), so as to keep itself on top for example, you can use a combination of associated values and some swizzling.
First, declare the child, and optionally, since I like to keep this kind of edgy solutions as isolated as possible, embed there a UIView private extension to add the KVO machinery (Swift 5.1 code):
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)
}
}
}
This way, you can keep your internal property observable, and aligned with any added view. This is just a quick implementation, there are many corner cases to handle (e.g: views added using insert or any other UIView methods and so on), but it's a starting point. Also, this can be tailored to different needs (observing siblings for example, not only parents, and so on).
subviews
. However, the system sendslayoutSubviews
to a view when it has gained or lost subviews (if the superview is in the on-screen view hierarchy). Maybe you can use a customUIView
subclass as the superview, and do what you need inlayoutSubviews
. If that's not sufficient, edit your question to include more details about why you want to be notified when the subview is removed. We can probably give you a better solution. – Meredith