swift - UIView 边界和框架的属性观察者 react 不同

标签 swift uiview properties bounds observers

虽然我正在探索观察 UIViewboundsframe 更改的选项(提到 herehere ) ,我遇到了一个非常奇怪的差异:didSetwillSet 将根据您将 UIView 放置在 View 中的位置以不同方式触发层次结构:

  • 如果我在 View Controller 的根UIView 使用属性观察器,我只会得到didSetwillSet 来自 frame 变化的事件。
  • 如果我为 View Controller 中的 UIView 使用属性观察器,它是一个 subview,我只会得到 didSetwillSet 来自 bounds 变化的事件。

我想首先指出,我明确避免提到 here 中的 KVO 方法因为官方不支持。我也不打算使用提到的 viewDidLayoutSubviews() here因为这不适用于观察 subview 的变化(参见 doc)。 这个问题假定我偏好使用 didSetwillSet 来观察 UIViewbounds/frame 更改。

我遇到的最接近的问题是 this question但是只涉及到初始化语句,也没有提到观察 subview 的情况。

详情

要查看实际效果,请查看 🔨 my sample project .

我真的很疑惑为什么有时不调用bounds观察者,所以我也添加了frame观察者,甚至有时会调用frame观察者没有叫。最终,我找到了它们工作方式不同的关键设置: View 在 View 层次结构中的位置,如上所述。

我如何测试:在这两种情况下,旋转设备以更改 View 的 frame/bounds

这是我的 UIView 子类:

公共(public)类 BoundsObservableView:UIView {

    public weak var boundsDelegate: ViewBoundsObserving?

    public override var bounds: CGRect {
        willSet {
            print("BOUNDS willSet bounds: \(bounds), frame: \(frame)")
            boundsDelegate?.boundsWillChange(self)
        }
        didSet {
            print("BOUNDS didSet bounds: \(bounds), frame: \(frame)")
            boundsDelegate?.boundsDidChange(self)
        }
    }

    public override var frame: CGRect {
        willSet {
            print("FRAME willSet frame: \(frame), bounds: \(bounds)")
            boundsDelegate?.boundsWillChange(self)
        }
        didSet {
            print("FRAME didSet frame: \(frame), bounds: \(bounds)")
            boundsDelegate?.boundsDidChange(self)
        }
    }
}

在我的示例代码中,如果您旋转设备,您会看到在我观察 Root View 的一种情况下(ViewControllerself.view -- 以蓝色显示),我永远不会收到 bounds 更改的通知,尽管它实际上已更改。 subview 则相反——我从未收到 frame 更改的通知,尽管它已经更改。

enter image description here enter image description here enter image description here

环境

我正在 iPad Air 和 iPad Pro 等设备上使用 iOS 11.4 SDK 在 Xcode 9.3 上测试这个项目。我还没有在 iOS 12 beta 上试过。

我的问题

  • UIView 在 View 层次结构中的位置不同时,为什么 didSetwillSet 的触发方式不同?
  • boundsdidSet 被触发时,为什么 framedidSet 也不会被触发(对于 subview )?反之亦然(对于 Root View )?
  • 如果有的话,有什么方法可以确保无论将 UIView 放在 View 层次结构中的哪个位置,我都能始终观察到 bounds 的变化?<

最佳答案

来 self 在 Apple Developer Forum 中的转发, QuinceyMorris 帮助我阐明了这种方法的问题,以及一种无论我将 View 放在 View 层次结构中的什么位置都有效的方法。

... an Obj-C property can change value without having its setter called. Changing the instance variable (of simple properties) is a very common Obj-C pattern. It is of course not KVO compliant without additional work, but that's why KVO compliance is not found universally.

... Your willSet/didSet accessors will only trigger when the change goes through their own property. There is nothing you can predict or assume about which property will be used. Even if you see a regularity now, there may be edge cases that are different, and the behavior may change in the future.

根据他建议我覆盖 layoutSubviews,这是我更新的子类(就像 this answer 一样):

public protocol ViewBoundsObserving: class {
    // Notifies the delegate that view's `bounds` has changed.
    // Use `view.bounds` to access current bounds
    func boundsDidChange(_ view: BoundsObservableView, from previousBounds: CGRect);
}

/// You can observe bounds change with this view subclass via `ViewBoundsObserving` delegate.
public class BoundsObservableView: UIView {
    
    public weak var boundsDelegate: ViewBoundsObserving?
    
    private var previousBounds: CGRect = .zero
    
    public override func layoutSubviews() {
        if (bounds != previousBounds) {
            print("Bounds changed from \(previousBounds) to \(bounds)")
            boundsDelegate?.boundsDidChange(self, from: previousBounds)
            previousBounds = bounds
        }
        
        // UIView's implementation will layout subviews for me using Auto Resizing mask or Auto Layout constraints.
        super.layoutSubviews()
    }
}

关于swift - UIView 边界和框架的属性观察者 react 不同,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50889512/

相关文章:

ios - 在 View 中查找所有 detailDislosureButtons

ios - 在 Swift 中自定义 AdColony V4VC 弹出菜单

ios - 添加到数组时无法推断通用参数 'Element'

iphone - 我可以使用 contentScaleFactor 安全地节省内存吗?

ios - 如何在 UIViewController 和 UIView 之间进行协调

c# - 无副作用二传手的方法

SwiftUI 列表 onTapGesture 覆盖了 NavigationLink

从内存中删除 UIView 时,swift deinit 方法不起作用

c# - 在此示例中使用 get 和 set 属性有什么好处?

java - Spring Boot 配置属性未设置