使用嵌入式视图了解自动布局生命周期和顺序
Posted
技术标签:
【中文标题】使用嵌入式视图了解自动布局生命周期和顺序【英文标题】:Understanding Auto Layout lifecycle and sequence with embedded views 【发布时间】:2020-07-24 02:22:00 【问题描述】:我有以下视图层次结构:
ViewController: UIViewController
|
+--- cardViewController: CardViewController (subclass of UIViewController)
|
+--- containerView: UIView
|
+--- menuTableView: MenuTableView (subclass of UITableView)
CardViewController
通过以下代码(从 ViewController 调用)显示在 ViewController
的视图中:
let cardViewController = CardViewController(startWithRoundedCorners: true)
self.addChild(cardViewController)
self.view.addSubview(cardViewController.view)
cardViewController.showCard()
在CardViewController
中,containerView
对视图的顶部、前导、尾随和底部锚点有约束。
反过来,menuTableView
也对containerView
的顶部前导、尾随和底部锚点有限制。
问题是这样的。 menuTableView.contentSize.height
仅在通过cellForRowAt
加载 tableView 单元格后计算。说得通。直到那个时间点,它看起来只是根据行数 * 44(默认大小)来估计 tableView 的高度。所以如果我检查CardViewController: viewDidLayoutSubviews()
中的menuTableView.contentSize.height
,我会返回264.0
。
我已经捕获了一些打印语句(一些被覆盖的方法带有“开始”和“完成”)来尝试计算出事情的运行顺序:
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
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)
所以据我所知,问题在于menuTableView.layoutSubviews
被调用,并且tableView.contentSize.height
正确地位于306.0
。但是到了这个阶段,父 viewController (CardViewController
) 已经完成了它的viewWillLayoutSubviews
和viewDidLayoutSubviews
。
问题
当子视图实际上仍在布置它们的视图时,父级如何完成其子视图的布置?根据 Apple 的文档,他们说自动布局循环是:
-
更新约束(自下而上,从子视图到父视图)
布局(自上而下)
绘图/显示
如果改变内部内容大小,在这种情况下,tableView 会导致自动布局约束发生更新,我不知道为什么这个循环不起作用。我玩过很多选项,有不同的触发方式,但我希望了解自动布局引擎,所以我不再遇到这些问题。
其他信息
有趣的是,如果我第二次调用cardViewController.showCard()
,那么它是正确的。额外的打印日志是:
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
很高兴让项目文件可用,如果这更容易的话。
【问题讨论】:
这是很多信息 - 但并不完全清楚。您是否尝试根据其.contentSize
设置表格视图的高度?
不,相反。 tableview 的高度应该基于它的内容。
好的 - 是一样的。你想要一个非滚动的表格视图吗?所以它会根据内容调整自己的大小?看看这个:***.com/a/57995132/6257435 ...如果你使用那个子类表视图,你可以像使用多行一样使用它UILabel
谢谢。我已经尝试过了,但它不起作用。它确实会导致 CardViewController.viewDidLayoutSubviews 再次被调用,但仍然无法呈现正确的结果。
我应该补充一点,根据我的第一批打印语句,tableView 的大小是正确的(它的 contentSize.height 是在 306 处计算的)。但我认为的问题是它没有触发父视图控制器 - CardViewController 中的任何自动布局方法。我不明白为什么不...
【参考方案1】:
抱歉,我无法添加评论 yet
所以我试着用这个answer section
作为问题。
你把这些放在哪里了?
let cardViewController = CardViewController(startWithRoundedCorners: true)
self.addChild(cardViewController)
self.view.addSubview(cardViewController.view)
cardViewController.showCard()
【讨论】:
我会更新问题,但它是从主 ViewController 调用的。以上是关于使用嵌入式视图了解自动布局生命周期和顺序的主要内容,如果未能解决你的问题,请参考以下文章
IOS / Autolayout:在生命周期中为代码中创建的元素设置约束
JetpackViewModel 架构组件 ( 视图 View 和 数据模型 Model | ViewModel 作用 | ViewModel 生命周期 | 代码示例 | 使用注意事项 )