简体   繁体   English

使用嵌入式视图了解自动布局生命周期和顺序

[英]Understanding Auto Layout lifecycle and sequence with embedded views

I have the following view hierarchy:我有以下视图层次结构:

ViewController: UIViewController 
|
+--- cardViewController: CardViewController (subclass of UIViewController)
     |
     +--- containerView: UIView
          |
          +--- menuTableView: MenuTableView (subclass of UITableView)
          

The CardViewController is shown on the ViewController 's view through the folloing code (called from ViewController): CardViewController通过以下代码(从 ViewController 调用)显示在ViewController的视图中:

  let cardViewController = CardViewController(startWithRoundedCorners: true)
  self.addChild(cardViewController)
  self.view.addSubview(cardViewController.view)
  cardViewController.showCard()

In CardViewController , the containerView has constraints to the view's top, leading, trailing, and bottom anchors.CardViewController中, containerView对视图的顶部、前导、尾随和底部锚点有约束。

In turn, the menuTableView also has constraints to the containerView s top leading, trailing and bottom anchors.反过来, menuTableView也对containerView的顶部前导、尾随和底部锚点有约束。

The issue is this.问题是这样的。 The menuTableView.contentSize.height is only being calculated after the tableView cells are loaded through cellForRowAt . menuTableView.contentSize.height仅在通过cellForRowAt加载 tableView 单元格后计算。 Makes sense.说得通。 Up until that point in time, it looks like it's just doing an estimation of the tableView height based on the number of rows * 44 (the default size).直到那个时间点,它看起来只是根据行数 * 44(默认大小)来估计 tableView 的高度。 So if I check the menuTableView.contentSize.height in CardViewController: viewDidLayoutSubviews() , I get back 264.0 .因此,如果我检查menuTableView.contentSize.height CardViewController: viewDidLayoutSubviews()中的 menuTableView.contentSize.height ,我会返回264.0

I've captured some print statements (with a 'started' and 'finished' for some of the overridden methods) to try and work out the order that things are running in:我已经捕获了一些打印语句(对于一些被覆盖的方法有一个“开始”和“完成”)来尝试计算出事情运行的顺序:

CardViewController: setuptableView(): started 
MenuTableView: convenience init(): started 
MenuTableView: override init(): started 
MenuTableView: numberOfRowsInSection: 
MenuTableView: override init(): self.frame.height: 0.0 
MenuTableView: override init(): self.contentSize.height: 0.0 
MenuTableView: override init(): finished 
MenuTableView: convenience init(): self.frame.height: 0.0 
MenuTableView: convenience init(): self.contentSize.height: 0.0 
MenuTableView: convenience init(): finished 
CardViewController: setuptableView(): finished
MenuTableView: numberOfRowsInSection: 
CardViewController: showCard(): self.frame.height: 0.0 
CardViewController: showCard(): self.contentSize.height: 264.0 
CardViewController: updateViewConstraints(): started 
CardViewController: updateViewConstraints(): finished  
CardViewController: viewWillLayoutSubviews(): started 
CardViewController: viewDidLayoutSubviews(): self.frame.height: 0.0 
CardViewController: viewDidLayoutSubviews(): self.contentSize.height: 264.0 
CardViewController: viewWillLayoutSubviews(): finished 
CardViewController: viewDidLayoutSubviews(): started  
CardViewController: viewDidLayoutSubviews(): self.frame.height: 0.0 
CardViewController: viewDidLayoutSubviews(): self.contentSize.height: 264.0 
CardViewController: viewDidLayoutSubviews(): finished 
MenuTableView: numberOfRowsInSection: 
MenuTableView: layoutSubviews(): started 
MenuTableView: numberOfRowsInSection: 
MenuTableView: cellForRowAt indexPath: [0, 0] 
MenuTableView: cellForRowAt indexPath: [0, 1] 
MenuTableView: cellForRowAt indexPath: [0, 2] 
MenuTableView: cellForRowAt indexPath: [0, 3] 
MenuTableView: cellForRowAt indexPath: [0, 4] 
MenuTableView: cellForRowAt indexPath: [0, 5] 
MenuTableView: layoutSubviews(): self.frame.height: 264.0 
MenuTableView: layoutSubviews(): self.contentSize.height: 306.0 
MenuTableView: layoutSubviews(): finished 
MenuTableView: layoutSubviews(): started 
MenuTableView: layoutSubviews(): self.frame.height: 264.0 
MenuTableView: layoutSubviews(): self.contentSize.height: 306.0 
MenuTableView: layoutSubviews(): finished

The code for the CardViewController: showCard() method is: CardViewController: showCard()方法的代码是:

UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 0.6, initialSpringVelocity: 1.0, options: .curveEaseInOut, animations: { [weak self] in
    let cardHeight = (self?.menuTableView.contentSize.height)! + self!.cardHandleAreaHeight
    self?.view.frame = CGRect(x: 0, y: (self?.parent?.view.frame.height)! - cardHeight, width: (self?.parent?.view.frame.width)!, height: cardHeight)
    
    }, completion: nil)

So from what I can tell, the problem is that the menuTableView.layoutSubviews gets called, and correctly has the tableView.contentSize.height at 306.0 .所以据我所知,问题是menuTableView.layoutSubviews被调用,并且tableView.contentSize.height正确地位于306.0 But by this stage, the parent viewController ( CardViewController ) has already finished with its viewWillLayoutSubviews and viewDidLayoutSubviews .但是到了这个阶段,父 viewController ( CardViewController ) 已经完成了它的viewWillLayoutSubviewsviewDidLayoutSubviews

Question问题

How can the parent be finished laying out its subviews when the subviews are actually still laying out their views??当子视图实际上仍在布置其视图时,父级如何完成其子视图的布置? Based on Apple's documentation, they say that the Auto Layout loop is:根据Apple的文档,他们说自动布局循环是:

  1. Update constraints (bottom-up, from subview to superview)更新约束(自下而上,从子视图到父视图)
  2. Layout (top-down)布局(自上而下)
  3. Drawing/Display绘图/显示

And if changes to the intrinsic content size of, in this case, the tableView cause an update to occur to the auto layout constraints, I'm at a loss why this loop doesn't work.如果在这种情况下更改 tableView 的内在内容大小会导致自动布局约束发生更新,我不知道为什么这个循环不起作用。 I've played around with a whole bunch of options and there are different ways to trigger it but I'm looking to understand the Auto Layout engine so I stop running into these issues.我玩过一大堆选项,并且有不同的方法来触发它,但我希望了解自动布局引擎,所以我不再遇到这些问题。

Additional Info附加信息

Interesting, if I call cardViewController.showCard() a second time, then it is correct.有趣的是,如果我再次调用cardViewController.showCard() ,那么它是正确的。 The additional print logs are then:额外的打印日志是:

CardViewController: showCard(): started
CardViewController: showCard(): self.frame.height: 278.0
CardViewController: showCard(): self.contentSize.height: 306.0
CardViewController: showCard(): finished
CardViewController: viewWillLayoutSubviews(): started
CardViewController: viewDidLayoutSubviews(): self.frame.height: 278.0
CardViewController: viewDidLayoutSubviews(): self.contentSize.height: 306.0
CardViewController: viewWillLayoutSubviews(): finished
CardViewController: viewDidLayoutSubviews(): started 
CardViewController: viewDidLayoutSubviews(): self.frame.height: 278.0
CardViewController: viewDidLayoutSubviews(): self.contentSize.height: 306.0
CardViewController: viewDidLayoutSubviews(): finished
MenuTableView: layoutSubviews(): started
MenuTableView: layoutSubviews(): self.frame.height: 306.0
MenuTableView: layoutSubviews(): self.contentSize.height: 306.0
MenuTableView: layoutSubviews(): finished

Happy to make the project file available if that's easier.如果这更容易,很高兴使项目文件可用。

sorry i can't add a comment yet so i tried to use this answer section as question.抱歉,我还不能添加yet所以我尝试将此answer section用作问题。

where did you put these?你把这些放在哪里了?

let cardViewController = CardViewController(startWithRoundedCorners: true)
  self.addChild(cardViewController)
  self.view.addSubview(cardViewController.view)
  cardViewController.showCard()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM