AutoLayout layoutIfNeeded 边界更新时间

AutoLayout layoutIfNeeded bounds update time

提问人:Deepak Sharma 提问时间:7/2/2023 最后编辑:Deepak Sharma 更新时间:7/3/2023 访问量:52

问:

我的自定义 UIView 中有以下代码。这从或初始值设定项调用:init(frame:)init(coder:)

   private func setupContentView() {
     contentView = ContentView(frame: .zero)
     contentView.translatesAutoresizingMaskIntoConstraints = false
     addSubview(contentView)
     contentView.leftAnchor.constraint(equalTo: leftAnchor, constant:thumbWidth).isActive = true
     contentView.rightAnchor.constraint(equalTo: rightAnchor, constant: -thumbWidth).isActive = true
     contentView.topAnchor.constraint(equalTo: topAnchor).isActive = true
     contentView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
     contentView.clipsToBounds = true
     contentView.isUserInteractionEnabled = true
     contentView.layoutIfNeeded()
     NSLog("ContentView bounds \(contentView.bounds), view bounds \(self.bounds)")
}

我知道调用时,它会在完成后立即更新帧。但我并不总是看到这种行为。下面的转储显示 的边界仍然是 0 宽度和 0 高度。我错过了什么?layoutIfNeededcontentViewNSLogcontentView

ContentView bounds (0.0, 0.0, 0.0, 0.0), view bounds (0.0, 0.0, 774.0, 84.0)
iOS Swift UIView UIKit自动 布局

评论

0赞 Fahim Parkar 7/2/2023
Timer.scheduledTimer(withTimeInterval: 0.5, repeats: false) { (t) in NSLog(“ContentView bounds (contentView.bounds), view bounds (self.bounds)”)} 尝试这样做,你会发现这两个值是相同的。因此,正如 Matt 所说,完全准备布局需要时间。
0赞 Deepak Sharma 7/2/2023
规定的最大延迟是多少?文档说它应该立即执行布局。
0赞 DonMag 7/2/2023
@DeepakSharma -- “我错过了什么?”我认为更好的问题是:“我们(从你的代码中)缺少什么?如果我试图猜测你在这里遗漏了什么:pastebin.com/WZ8uDVd5 - 日志输出确实显示了结果边界。
0赞 Deepak Sharma 7/2/2023
@DonMag 我在我的项目中看到的是,超级视图首先通过故事板初始化,然后我调用该方法。主要的争论点是我所期望的是真实的还是错误的。init(coder:)setupContentView()
2赞 DonMag 7/2/2023
@DeepakSharma - 你问只是因为你好奇吗?或者因为您想对生成的帧/边界信息执行某些操作?如果是后者,我想我的答案是:“不要那样做。这不是评估这些值的正确位置。

答:

2赞 matt 7/2/2023 #1

如果您想知道布局后内容视图的边界是什么,那么需要布局的不是内容视图,而是它的监督视图。改变

 contentView.layoutIfNeeded()

 contentView.superview?.layoutIfNeeded()

甚至更可靠,比如说

contentView.superview?.layoutIfNeeded()
CATransaction.setCompletionBlock {
    NSLog("ContentView bounds \(contentView.bounds), view bounds \(self.bounds)")
}

这可确保在登录之前布局已完成

评论

0赞 Deepak Sharma 7/2/2023
这两个部分都给出了不正确的结果。当高度为 0 时,甚至不正确。self.boundsContentView bounds (0.0, 0.0, 0.0, 0.0), view bounds (0.0, 0.0, 28.0, 0.0)
1赞 DonMag 7/2/2023
嗯......如果你在那个完成块中得到边界,这不是多余的吗?contentView.superview?.layoutIfNeeded()
1赞 DonMag 7/2/2023 #2

您的自定义子类不会“单独存在”。UIView

让我们从一个简单的“内容视图”子类开始:

class ContentView: UIView {
}

我们将定义一个自定义视图,如下所示:

class YourCustomView: UIView {
    
    var contentView: ContentView!
    let thumbWidth: CGFloat = 40.0
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        print(#function)
        setupContentView()
    }
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        print(#function)
        setupContentView()
    }
    
    private func setupContentView() {
        contentView = ContentView(frame: .zero)
        contentView.translatesAutoresizingMaskIntoConstraints = false
        addSubview(contentView)
        contentView.leftAnchor.constraint(equalTo: leftAnchor, constant:thumbWidth).isActive = true
        contentView.rightAnchor.constraint(equalTo: rightAnchor, constant: -thumbWidth).isActive = true
        contentView.topAnchor.constraint(equalTo: topAnchor).isActive = true
        contentView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
        contentView.clipsToBounds = true
        contentView.isUserInteractionEnabled = true
        
        contentView.layoutIfNeeded()
        NSLog("A: ContentView bounds \(contentView.bounds), view bounds \(self.bounds)")

        self.layoutIfNeeded()
        NSLog("B: ContentView bounds \(contentView.bounds), view bounds \(self.bounds)")

        self.superview?.layoutIfNeeded()
        NSLog("C: ContentView bounds \(contentView.bounds), view bounds \(self.bounds)")
        
        CATransaction.setCompletionBlock {
            NSLog("D: ContentView bounds \(self.contentView.bounds), view bounds \(self.bounds)")
        }
    }
    
}

现在,在 Storyboard 中,我们将添加一个 ,将其类赋给 ,并为其提供 top/leading/bottom/constraints of :UIViewYourCustomView80

enter image description here

运行时,我们得到以下输出:

init(coder:)
A: ContentView bounds (0.0, 0.0, 0.0, 0.0), view bounds (0.0, 0.0, 233.0, 599.0)
B: ContentView bounds (0.0, 0.0, 0.0, 0.0), view bounds (0.0, 0.0, 80.0, 0.0)
C: ContentView bounds (0.0, 0.0, 0.0, 0.0), view bounds (0.0, 0.0, 80.0, 0.0)
D: ContentView bounds (0.0, 0.0, 153.0, 599.0), view bounds (0.0, 0.0, 233.0, 599.0)

接下来,我们将给它明确的宽度/高度约束,而不是尾随/底部约束:

enter image description here

现在,当我们运行它时,我们得到这个:

init(coder:)
A: ContentView bounds (0.0, 0.0, 120.0, 500.0), view bounds (0.0, 0.0, 200.0, 500.0)
B: ContentView bounds (0.0, 0.0, 120.0, 500.0), view bounds (0.0, 0.0, 200.0, 500.0)
C: ContentView bounds (0.0, 0.0, 120.0, 500.0), view bounds (0.0, 0.0, 200.0, 500.0)
D: ContentView bounds (0.0, 0.0, 120.0, 500.0), view bounds (0.0, 0.0, 200.0, 500.0)

正如我们所看到的,在第一种情况下 - 我们设置尾部/底部 - 在自动布局期间还不知道最终的宽度和高度是多少。init(coder)YourCustomView

在第二种情况下 - 我们设置宽度/高度 - 在自动布局期间已经知道最终的宽度和高度是多少。init(coder)

请注意,如果我回到 Storyboard 并使用以下方法进行布局:iPhone SE (1st Generation

enter image description here

并在 上运行应用程序,我们得到以下输出:iPhone 14 Pro

init(coder:)
A: ContentView bounds (0.0, 0.0, 0.0, 0.0), view bounds (0.0, 0.0, 160.0, 388.0)
B: ContentView bounds (0.0, 0.0, 0.0, 0.0), view bounds (0.0, 0.0, 80.0, 0.0)
C: ContentView bounds (0.0, 0.0, 0.0, 0.0), view bounds (0.0, 0.0, 80.0, 0.0)
D: ContentView bounds (0.0, 0.0, 153.0, 599.0), view bounds (0.0, 0.0, 233.0, 599.0)

请注意,这次自定义视图边界从以下位置开始

(0.0, 0.0, 160.0, 388.0)

它是从我的故事板布局中拾取的。

文档说:.layoutIfNeeded()

使用此方法可强制视图立即更新其布局。使用“自动布局”时,布局引擎会根据需要更新视图的位置,以满足约束的变化。

您遇到的混淆是视图层次结构的其余部分可能未初始化...并且可能(可能会)改变在您拨打该电话之后。