从 xib 加载视图控制器返回错误的帧

Posted

技术标签:

【中文标题】从 xib 加载视图控制器返回错误的帧【英文标题】:Loading View Controller from xib return wrong frame 【发布时间】:2020-12-18 06:54:13 【问题描述】:

我在从 xib 加载视图控制器时遇到问题

我有一个 xib 文件,其中包含一个视图控制器和一个子视图,如下图所示。请注意,我在 xib 中使用 iPhone 11 Pro Max(屏幕宽度 = 414)

子视图有 8 个到其父视图的前导和尾随。

问题是当我在 iPhone 12 Pro Max(屏幕宽度 = 428)上运行应用程序时,我检查屏幕尺寸和子视图尺寸的值,它返回奇怪的值。这是我的代码:


public override func viewDidLoad() 
        super.viewDidLoad()
        
        print(view.frame) // (0.0, 0.0, 414.0, 896.0)
        print(collageView.frame) // (8.0, 210.0, 398.0, 398.0)

    
    
    public override func viewDidLayoutSubviews() 
        super.viewDidLayoutSubviews()
        
        print(view.frame) // (0.0, 0.0, 428.0, 926.0) updated
        print(collageView.frame) // (8.0, 210.0, 398.0, 398.0) ??
        
        let width = collageView.frame.width
        let height = collageView.frame.height
        
        let frames = self.frames?(CGSize(width: width, height: height)) ?? []
        
        for i in 0..<frames.count 
            let componentView = CropComponentView(frame: frames[i])
            componentView.image = images[i]
            collageView.addSubview(componentView)
        
    
  

我的问题是:

我们如何在 viewDidLoad 中获得正确的屏幕尺寸?为什么子视图的框架不像屏幕大小那样更新?

【问题讨论】:

您无法在viewDidLoad 中获取任何尺寸信息。太早了。 programmingios.net/premature-layout @matt 我接受这一点,但是关于子视图框架的问题如何。子视图如何有正确的框架? @TungVuDuc 试试layoutIfNeeded(),然后在 viewdidload 中检查框架。让我知道它是否有效 @dahiya_boy call layoutIfNeeded() inside viewDidLayoutSubviews bellow super.viewDidLayoutSubviews() 然后collageView的框架被更新 另外,这并不重要,但您应该删除 Center X 约束,因为它不是必需的。如果视图具有彼此相等的前导和尾随约束,则根据定义将其水平居中。 【参考方案1】:

我们如何在 viewDidLoad 中获得正确的屏幕尺寸?

排序答案是您无法在 viewDidLoad() 中获得最终视图(viewController 的)大小,因为该视图刚刚从 xib 加载并且具有在 xib 中设置的占位符框架。需要将其添加到视图层次结构中(例如添加到父视图/窗口),然后才能应用布局规则。视图将在视图加载后立即添加。

控制器可用的宽度/高度由容器控制器(如果有)以及设备方向和状态栏状态决定。例如,如果您的控制器被添加到 tabbarcontroller 中,那么如果 tabbar 正在显示,它将具有更少的垂直空间。如果显示状态栏并且您有来电,则状态栏的大小将发生变化,留下更少的垂直空间。如果你在 SplitViewController 容器中显示你的控制器,那么它的宽度会更小。

如果您假设它将占用整个屏幕尺寸,您可以在 viewDidLoad() 中计算帧:

    override func viewDidLoad()  
       //...
       var screenSize = UIScreen.main.bounds
       var collageViewFrame: CGSize = self.collageView.frame.sizeThatFits(screenSize)
     

为什么子视图的框架没有像屏幕大小那样更新?

从documentation 到-viewDidLayoutSubviews

但是,调用此方法并不表示 视图的子视图的个别布局已调整。

如果您需要正确的框架,您需要在该子视图中调用layoutIfNeeded 方法。 还请this回答。

【讨论】:

Interface Builder 上显然没有丢失/错误约束的迹象,因此 1:1 的比例可能在其自身,因此所有约束都得到满足。 @Kalzem 你是对的。看来 viewDidLayoutSubviews 并不表示视图的子视图的个别布局已经调整。在答案***.com/questions/28601742/… 中查看更多信息。我会更新并更正我的答案 所以在阅读您的参考后,我们是否应该将所有自动布局设置移动到 viewDidAppear 以保证所有帧都正确? @ChristosKoninis 是的,要么将其移动到 viewDidAppear,要么如果您知道超级视图的大小,您可以调用方法 sizeThatFits(SizeOfSuperView) 来获取框架,在大多数情况下,超级视图的大小是 UIScreen.main。界限

以上是关于从 xib 加载视图控制器返回错误的帧的主要内容,如果未能解决你的问题,请参考以下文章

从 xib 错误加载表格视图中的单元格

从 xib 加载 iOS 不起作用

如何从 xib 文件加载视图控制器?

UIViewController 通过 xib 加载另一个视图控制器,尝试访问默认视图导致崩溃

xib 指定的位置在 UIView 加载时移动?

如何从 XIB 文件加载自定义创建的视图并添加到 StackView