在 Swift 中获取布局后的 UIScrollView 的高度

Get the height of the UIScrollView after layout in Swift

我有一种情况,我正在使用 UIScrollView 来帮助使用较小手机的用户查看 window 中的完整文本。如果他们需要滚动我想显示一个图像图标(只是为了让他们知道 window 里面还有更多)但是我在使用 Swift 找到滚动视图的高度时遇到了问题.

这是我的对象的层次结构

基本上,这 4 个标签适合 4.7" iPhone 而不是 4" iPhone。我尝试设置一些逻辑来检查 UIScrollView 内容是否大于 window,但它似乎不起作用。我一直得到高度为零。

我正在使用 scrollHeight = scrollView.contentSize.height 并尝试将它放在 viewDidLoad()viewWillAppear()viewDidLayoutSubviews() 中,但每次它最终都是 0。

我应该使用 contentSize 来检查身高吗?

TL;DR;

作为一般指南,自动布局在 viewDidLoad 被调用时并没有完成视图的大小设置(在 viewDidLayoutSubviews 中也不正确)。要确保所有视图及其子视图在查询它们的 boundsframe 之前具有正确的大小,请执行:

self.view.setNeedsLayout()
self.view.layoutIfNeeded()
print(self.someSubview.frame) // gives correct frame

说明

有点违反直觉,将您的代码放在 viewDidLayoutSubviews 中并不意味着自动布局已为所有子视图指定了正确的大小。引用 the docs for this method:

However, this method being called does not indicate that the individual layouts of the view's subviews have been adjusted.

这意味着:在 viewDidLayoutSubviews 中,只有第一层级的子视图具有正确的大小(即子视图而不是 "grandchild views")。


这是我创建的示例布局(红色视图是子视图,蓝色视图是 g运行dchild)。

关于这个例子的几个要点:

  • 所有视图都使用自动布局约束定位到它们的父视图(固定边距,但不是固定宽度),这允许它们根据屏幕尺寸扩大或缩小模拟器我运行这个就上了。
  • 我在故事板中使用了 iPhone 7 大小的视图控制器,运行 在 iPhone 7 Plus 模拟器上使用了示例。这意味着我可以确定视图会有所增长,并使用它来确定视图是否具有其原始大小或最终大小。

请注意,在 Interface Builder 中,子视图(红色)的原始宽度为 240 点,g运行d 子视图(蓝色)的原始宽度为 200 点。

这是代码:

class ViewController: UIViewController {

    @IBOutlet weak var childView: UIView!
    @IBOutlet weak var grandchildView: UIView!

    override func viewDidLoad() {
        super.viewDidLoad()
        print("child     ", #function, childView.frame.size.width)
        print("grandchild", #function, grandchildView.frame.size.width)
    }

    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        print("child     ", #function, childView.frame.size.width)
        print("grandchild", #function, grandchildView.frame.size.width)
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        print("child     ", #function, childView.frame.size.width)
        print("grandchild", #function, grandchildView.frame.size.width)
    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        print("child     ", #function, childView.frame.size.width)
        print("grandchild", #function, grandchildView.frame.size.width)
    }
}

这是输出:

child      viewDidLoad() 240.0
grandchild viewDidLoad() 200.0
child      viewWillAppear 240.0
grandchild viewWillAppear 200.0
child      viewDidLayoutSubviews() 271.0 // `child` has it's final width now
grandchild viewDidLayoutSubviews() 200.0 // `grandchild` still has it's original width!

// By now, in MOST cases, all views will have their correct sizes.
child      viewDidAppear 271.0
grandchild viewDidAppear 231.0

从这个输出中您可以看到,viewDidLayoutSubviews 不会 gua运行看到视图控制器的直接子视图之后的任何内容主视图具有正确的大小。

注意: 通常在调用 viewDidAppear 时,视图具有正确的大小,但我想说这绝不是 gua运行teed.