我已经四处寻找答案,我试图实现它,但没有任何工作。基本上,我需要能够观察VC视图的子视图数组中的更改。如果从该数组中删除现有视图,我希望收到有关它的通知并运行一些代码。
有可能吗?
编辑 - 更多信息
我正在尝试修复一个奇怪的边缘案例错误,快速点击UISearchDisplayController(非常自定义)的UISearchBar导致sdController(或者更确切地说是navBar效果中的托管搜索栏)从视图中消失,但是sdController仍处于活动状态。这意味着navBar停留在-y原点,下面的tableView不可滚动。
我最初的想法是进入sdController处于活动状态,但UISearchDisplayControllerContainerView不在视图层次结构中。我尝试在VC的viewDidLayoutSubviews中测试它,但是当你点击搜索栏并启动sdController动画时,sdController处于活动状态,而UISearchDisplayControllerContainerView不在视图层次结构中:(。 / p>
答案 0 :(得分:15)
您可以观察符合KVO的CALayer的属性sublayers
,而不是UIView subviews
。
答案 1 :(得分:3)
与Apple框架中的大多数属性一样subviews
is not KVO compliant。
如果您控制子视图或超级视图,您可以通过子类化和覆盖来观察视图层次结构的更改:
在超级视图中,您必须覆盖...
- (void)willRemoveSubview:(UIView *)subview
...或者,如果您控制子视图,则会覆盖...
- (void)willMoveToSuperview
在删除视图之前调用这两种方法。
答案 2 :(得分:1)
Swift 3.x
使用自定义视图
class ObervableView: 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
}
}
答案 3 :(得分:0)
好吧,我偶然发现了类似的情况。基本上,如果您需要一个视图来观察它的父子视图数组,甚至它的大小或对其的任何更改(不符合 KVO 标准),以便保持自己在顶部,例如,您可以使用关联值和一些调酒。
首先,声明子项,并且可选地,因为我喜欢保持这种前卫的解决方案尽可能孤立,所以在那里嵌入一个 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)
}
}
}
通过这种方式,您可以保持内部属性可观察,并与任何添加的视图保持一致。这只是一个快速实现,有许多极端情况需要处理(例如:使用插入或任何其他 UIView 方法添加的视图等),但它是一个起点。此外,这可以根据不同的需求量身定制(例如,观察兄弟姐妹,不仅仅是父母等等)。