【问题标题】:How to detect when UIView size is changed in swift ios xcode?如何在swift ios xcode中检测UIView大小何时更改?
【发布时间】:2015-08-17 10:42:20
【问题描述】:

我正在 UIScrollView 中使用 CATiledLayer 尝试一些东西。

不知何故,UIScrollView 内的 UIView 的大小变成了一个很大的数字。我需要找出导致这种调整大小的确切原因。

有没有办法检测 UIView 的大小(框架、边界)或 UIScrollView 的 contentSize 何时调整?

我试过了

override var frame: CGRect {
    didSet {
        println("frame changed");
    }
}

在 UIView 子类中,

但它只在应用启动时调用一次,虽然 UIView 的大小在之后会调整大小。

【问题讨论】:

标签: ios swift cocoa-touch uiview uiscrollview


【解决方案1】:

viewWillLayoutSubviews()viewDidLayoutSubviews() 将在边界发生变化时被调用。在视图控制器中。

【讨论】:

  • 这是UIView**Controller** 方法,而不是像请求的UIView 方法。
【解决方案2】:

答案是正确的,尽管就我而言,我在情节提要中设置的约束导致 UIView 大小发生变化而没有回调任何检测函数。

【讨论】:

  • 也许我理解错了。但是 AFAIK 由于约束而发生的任何事情都应该显示在布局预览中 - 在助手编辑器中。
【解决方案3】:

你也可以使用KVO:

您可以像这样设置 KVO,其中 view 是您要观察帧变化的 view

self.addObserver(view, forKeyPath: "center", options: NSKeyValueObservingOptions.New, context: nil)

您可以通过此通知获取更改:

override func observeValueForKeyPath(keyPath: String!, ofObject object: AnyObject!, change: NSDictionary!, context: CMutableVoidPointer) {
    }

observeValueForKeyPath 将在您观察的视图框架发生变化时被调用。

还记得在视图即将被释放时移除观察者:

view.removeObserver(self, forKeyPath:"center")

【讨论】:

  • UIKit 上的 KVO 可能随时中断。所以应该只作为最后的手段使用,并为它在未来的 iOS 版本中失败做好准备。看看来自 iOS UIKit 工程师的这个答案:stackoverflow.com/a/6051404/251394
  • UIKit 属性通常不符合 KVO,也没有文档证明是这样。我会远离这种方法。
【解决方案4】:

第 1 步:viewWillLayoutSubviews

调用来通知视图控制器它的视图即将 布局其子视图

当视图的边界发生变化时,视图会调整其位置 子视图。您的视图控制器可以覆盖此方法以使 在视图布置其子视图之前更改。默认 这个方法的实现什么都不做。

第 2 步:viewDidLayoutSubviews

调用以通知视图控制器其视图刚刚布局 它的子视图。

当视图控制器视图的边界发生变化时,视图 调整其子视图的位置,然后系统调用它 方法。但是,调用此方法并不表示 视图的子视图的个别布局已调整。每个 subview 负责调整自己的布局。

您的视图控制器可以覆盖此方法以在之后进行更改 视图列出了它的子视图。这个的默认实现 方法什么都不做。

只要 UIView 的边界发生变化,就会调用以上这些方法

【讨论】:

    【解决方案5】:

    这里有答案:

    https://stackoverflow.com/a/27590915/5160929

    只需将其粘贴到方法主体之外:

    override var bounds: CGRect {
        didSet {
            // Do stuff here
        }
    }
    

    【讨论】:

    • 最适合 UIScrollView (因为 viewWillLayoutSubviews() 不可访问)注意它可能会被多次调用,注意不要改变它的边界,它会是无限的
    • 检测bounds 更改不能保证有效,具体取决于您将视图放在视图层次结构中的位置。我会改写layoutSubviews。请参阅 thisthis 答案。
    【解决方案6】:

    您可以创建一个自定义类,并使用闭包轻松获取更新后的矩形。在处理类时特别方便(比如CAGradientLayer,它希望你给他们一个CGRect):

    GView.swift:

    import Foundation
    import UIKit
    
    class GView: UIView {
    
        var onFrameUpdated: ((_ bounds: CGRect) -> Void)?
    
        override func layoutSublayers(of layer: CALayer) {
          super.layoutSublayers(of: layer)
          self.onFrameUpdated?(self.bounds)
        }
    }
    

    示例用法:

    let headerView = GView()
    let gradientLayer = CAGradientLayer()
    headerView.layer.insertSublayer(gradientLayer, at: 0)
        
    gradientLayer.colors = [
        UIColor.mainColorDark.cgColor,
        UIColor.mainColor.cgColor,
    ]
        
    gradientLayer.locations = [
        0.0,
        1.0,
    ]
        
    gradientLayer.startPoint = CGPoint(x: 0.0, y: 0.0)
    gradientLayer.endPoint = CGPoint(x: 1.0, y: 1.0)
        
    headerView.onFrameUpdated = { _ in // here you have access to `bounds` and `frame` with proper values
        gradientLayer.frame = headerView.bounds
    }
    

    如果不是通过代码添加视图,可以将storyboard中的Custom Class属性设置为GView

    请注意,名称GView 是作为公司衡量标准选择的,可能选择FrameObserverView 之类的名称会更好。

    【讨论】:

      【解决方案7】:

      对于 UIViews,很简单:

          override func layoutSubviews() {
              super.layoutSubviews()
      
              setupYourNewLayoutHereMate()
          }
      

      【讨论】:

      • 没那么容易。 layoutSubviews 可以多次调用。此外,视图的framebounds 属性可能是.zero。所以你必须在每次调用时检查它,并采取适当的行动(例如删除/重建层)
      【解决方案8】:

      您可以使用FrameObserver Pod。

      它没有使用 KVO 或 Method Swizzling,因此即使 UIKit 的底层实现发生变化也不会破坏您的代码。

      whateverUIViewSubclass.addFrameObserver { frame, bounds in // get updates when the size of view changes
          print("frame", frame, "bounds", bounds)
      }
      

      您可以在 UIView 实例或其任何子类上调用它,例如 UILabelUIButtonUIStackView 等。

      【讨论】:

        【解决方案9】:

        这是一个简单且不太老套的解决方案:您记住视图的最后大小,将其与覆盖的 layoutSubviews 方法中的新大小进行比较,然后在确定大小已更改时执行某些操作.

        /// Create this as a private property in your UIView subclass
        private var lastSize: CGSize = .zero
        
        open override func layoutSubviews() {
            // First call super to let the system update the layout
            super.layoutSubviews()
            
            // Check if:
            // 1. The view is part of the view hierarchy
            // 2. Our lastSize var doesn't still have its initial value
            // 3. The new size is different from the last size
            if self.window != nil, lastSize != .zero, frame.size != lastSize {
                // React to the size change
            }
            
            lastSize = frame.size
        }
        

        请注意,您不必包括self.window != nil 检查,但我假设在大多数情况下,您只对作为视图层次结构一部分的视图的大小更改感兴趣。

        享受吧!

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2014-12-29
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-11-02
          • 2013-09-27
          相关资源
          最近更新 更多