基于 Child 视图控制器的动态大小视图
Dynamically Sized View based on Child View Controller
我有一个自定义视图控制器用作 child 视图控制器:
class ChildViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .green
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
calculatePreferredSize()
}
func calculatePreferredSize() {
let targetSize = CGSize(width: view.bounds.width,
height: UIView.layoutFittingCompressedSize.height)
preferredContentSize = view.systemLayoutSizeFitting(targetSize)
}
}
然后在主视图控制器中,我有这段代码:
class ViewController: UIViewController {
var container : UIView!
var childVC : ChildViewController!
var containerHeightConstraint: NSLayoutConstraint!
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .purple
// setup container to hold child vc
container = UIView()
container.backgroundColor = .systemPink
container.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(container)
container.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor).isActive = true
container.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20).isActive = true
container.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20).isActive = true
containerHeightConstraint = NSLayoutConstraint()
containerHeightConstraint = container.heightAnchor.constraint(equalToConstant: 0)
containerHeightConstraint.isActive = true
// setup child vc
childVC = ChildViewController()
addChild(childVC)
container.addSubview(childVC.view)
childVC.view.frame = container.bounds
childVC.didMove(toParent: self)
// add contents into the child vc
let newView = UIView()
childVC.view.addSubview(newView)
newView.backgroundColor = .systemBlue
newView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
newView.topAnchor.constraint(equalTo: newView.superview!.topAnchor),
newView.leadingAnchor.constraint(equalTo: newView.superview!.leadingAnchor),
newView.trailingAnchor.constraint(equalTo: newView.superview!.trailingAnchor),
newView.heightAnchor.constraint(equalToConstant: 123),
])
}
override func preferredContentSizeDidChange(forChildContentContainer container: UIContentContainer) {
super.preferredContentSizeDidChange(forChildContentContainer: container)
if (container as? ChildViewController) != nil {
containerHeightConstraint.constant = container.preferredContentSize.height
}
}
}
我正在尝试根据 child 的计算高度在主 VC 中动态调整 container
视图的大小。正在调用 preferredContentSizeDidChange
方法,但在我计算 child VC 的高度(使用 UIView.layoutFittingCompressedSize
)时,我总是返回 0。即使我'已经检查了添加到该视图的视图框架,它具有正确的框架高度(在本例中为 123)。如下面的输出日志所示:
(lldb) po view.subviews
▿ 1 element
- 0 : <UIView: 0x12251cd40; frame = (0 0; 350 123); layer = <CALayer: 0x6000007a0e60>>
(lldb) po UIView.layoutFittingCompressedSize
▿ (0.0, 0.0)
- width : 0.0
- height : 0.0
下面是模拟器的截图。
我使用 UIView.layoutFittingCompressedSize
不正确吗?如何根据内容计算 child 视图的高度?
Autolayout 无法计算 newView
内容高度,因为它缺少 Y 轴上的约束来求解方程。
newView
只定义了这些约束:顶部、前导、尾随和高度。
它缺少底部约束:
newView.bottomAnchor.constraint(equalTo: newView.superview!.bottomAnchor).isActive = true
完整的约束集如下所示:
NSLayoutConstraint.activate([
newView.topAnchor.constraint(equalTo: newView.superview!.topAnchor),
newView.leadingAnchor.constraint(equalTo: newView.superview!.leadingAnchor),
newView.trailingAnchor.constraint(equalTo: newView.superview!.trailingAnchor),
newView.heightAnchor.constraint(equalToConstant: 123),
newView.bottomAnchor.constraint(equalTo: newView.superview!.bottomAnchor)
])
然后当我在 preferredContentSizeDidChange
中放置断点时,我可以打印 container.preferredContentSize.height
,即 123.0
。
编辑
为了避免约束破坏,我们还需要为 childVC.view
使用自动布局。现在它正在使用自动调整掩码,它只从上到下流动并创建优先级为 1000 的约束。
childVC.view.frame = container.bounds
需要替换为
childVC.view.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
childVC.view.topAnchor.constraint(equalTo: container.topAnchor),
childVC.view.leadingAnchor.constraint(equalTo: container.leadingAnchor),
childVC.view.trailingAnchor.constraint(equalTo: container.trailingAnchor),
childVC.view.bottomAnchor.constraint(equalTo: container.bottomAnchor)
])
和 containerHeightConstraint
需要降低 0 高度约束的优先级,否则系统总是会发现约束不明确——子控制器想要 123 点高,但容器高度约束是在调用 preferredContentSizeDidChange
方法之前仍为 0。
containerHeightConstraint.priority = .defaultLow
我有一个自定义视图控制器用作 child 视图控制器:
class ChildViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .green
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
calculatePreferredSize()
}
func calculatePreferredSize() {
let targetSize = CGSize(width: view.bounds.width,
height: UIView.layoutFittingCompressedSize.height)
preferredContentSize = view.systemLayoutSizeFitting(targetSize)
}
}
然后在主视图控制器中,我有这段代码:
class ViewController: UIViewController {
var container : UIView!
var childVC : ChildViewController!
var containerHeightConstraint: NSLayoutConstraint!
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .purple
// setup container to hold child vc
container = UIView()
container.backgroundColor = .systemPink
container.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(container)
container.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor).isActive = true
container.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20).isActive = true
container.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20).isActive = true
containerHeightConstraint = NSLayoutConstraint()
containerHeightConstraint = container.heightAnchor.constraint(equalToConstant: 0)
containerHeightConstraint.isActive = true
// setup child vc
childVC = ChildViewController()
addChild(childVC)
container.addSubview(childVC.view)
childVC.view.frame = container.bounds
childVC.didMove(toParent: self)
// add contents into the child vc
let newView = UIView()
childVC.view.addSubview(newView)
newView.backgroundColor = .systemBlue
newView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
newView.topAnchor.constraint(equalTo: newView.superview!.topAnchor),
newView.leadingAnchor.constraint(equalTo: newView.superview!.leadingAnchor),
newView.trailingAnchor.constraint(equalTo: newView.superview!.trailingAnchor),
newView.heightAnchor.constraint(equalToConstant: 123),
])
}
override func preferredContentSizeDidChange(forChildContentContainer container: UIContentContainer) {
super.preferredContentSizeDidChange(forChildContentContainer: container)
if (container as? ChildViewController) != nil {
containerHeightConstraint.constant = container.preferredContentSize.height
}
}
}
我正在尝试根据 child 的计算高度在主 VC 中动态调整 container
视图的大小。正在调用 preferredContentSizeDidChange
方法,但在我计算 child VC 的高度(使用 UIView.layoutFittingCompressedSize
)时,我总是返回 0。即使我'已经检查了添加到该视图的视图框架,它具有正确的框架高度(在本例中为 123)。如下面的输出日志所示:
(lldb) po view.subviews
▿ 1 element
- 0 : <UIView: 0x12251cd40; frame = (0 0; 350 123); layer = <CALayer: 0x6000007a0e60>>
(lldb) po UIView.layoutFittingCompressedSize
▿ (0.0, 0.0)
- width : 0.0
- height : 0.0
下面是模拟器的截图。
我使用 UIView.layoutFittingCompressedSize
不正确吗?如何根据内容计算 child 视图的高度?
Autolayout 无法计算 newView
内容高度,因为它缺少 Y 轴上的约束来求解方程。
newView
只定义了这些约束:顶部、前导、尾随和高度。
它缺少底部约束:
newView.bottomAnchor.constraint(equalTo: newView.superview!.bottomAnchor).isActive = true
完整的约束集如下所示:
NSLayoutConstraint.activate([
newView.topAnchor.constraint(equalTo: newView.superview!.topAnchor),
newView.leadingAnchor.constraint(equalTo: newView.superview!.leadingAnchor),
newView.trailingAnchor.constraint(equalTo: newView.superview!.trailingAnchor),
newView.heightAnchor.constraint(equalToConstant: 123),
newView.bottomAnchor.constraint(equalTo: newView.superview!.bottomAnchor)
])
然后当我在 preferredContentSizeDidChange
中放置断点时,我可以打印 container.preferredContentSize.height
,即 123.0
。
编辑
为了避免约束破坏,我们还需要为 childVC.view
使用自动布局。现在它正在使用自动调整掩码,它只从上到下流动并创建优先级为 1000 的约束。
childVC.view.frame = container.bounds
需要替换为
childVC.view.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
childVC.view.topAnchor.constraint(equalTo: container.topAnchor),
childVC.view.leadingAnchor.constraint(equalTo: container.leadingAnchor),
childVC.view.trailingAnchor.constraint(equalTo: container.trailingAnchor),
childVC.view.bottomAnchor.constraint(equalTo: container.bottomAnchor)
])
和 containerHeightConstraint
需要降低 0 高度约束的优先级,否则系统总是会发现约束不明确——子控制器想要 123 点高,但容器高度约束是在调用 preferredContentSizeDidChange
方法之前仍为 0。
containerHeightConstraint.priority = .defaultLow