在 StackView 中动态添加 ViewControllers - UIScrollView 中的 UIStackView
Adding dynamically ViewControllers inside StackView - UIStackView inside UIScrollView
我想在 UIStackView
中动态添加视图控制器。
UIStackView
必须是可滚动的,所以我将它添加到 UIScrollView
.
中
我有一个 UI 错误,内容大小不明确,我尝试使用 Debug View Hierarchy
进行调试,结果如下:
UIStackView
中每个控制器的大小是.zero
--
这是故事板上的层次结构
约束条件:
ScrollView
.leading
= StackView
.leading
ScrollView
.trailing
= StackView
.trailing
ScrollView
.top
= StackView
.top
ScrollView
.bottom
= StackView
.bottom
ScrollView
.width
= StackView
.width
ScrollView
.height
= StackView
.height
我的源代码:
class ScrollViewController: UIViewController {
@IBOutlet weak var scrollView: UIScrollView!
@IBOutlet weak var stackView: UIStackView!
private var strings = ["echo", "hola", "allo"]
private var containerViews = [ContainerView]()
override func viewDidLoad() {
super.viewDidLoad()
self.setupViews()
}
private func setupViews() {
self.setupContainers()
}
fileprivate func removeContainers() {
for container in containerViews {
container.uninstall()
}
containerViews.removeAll()
}
fileprivate func setupContainers() {
removeContainers()
for string in strings {
let viewController = // get the view Controller from StoryBoard
add(viewController)
}
}
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let viewController = segue.destination
let index = strings.index(of: sender as! String)!
}
fileprivate func add(_ viewController: UIViewController) {
let containerView = ContainerView(parentController: self)
containerView.install(viewController)
stackView.addArrangedSubview(containerView)
}
}
class ContainerView<T:UIViewController>: UIView {
unowned var parentViewController: UIViewController
weak var currentController: T?
init(parentController: UIViewController) {
self.parentViewController = parentController
super.init(frame: CGRect.zero)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func install(_ viewController: T) {
pushViewController(viewController, animated: false)
}
func uninstall() {
if let controller = currentController {
removeViewController(controller)
currentController = nil
}
}
fileprivate func setUpViewController(_ targetViewController: T?, animated: Bool) {
if let viewController = targetViewController {
parentViewController.addChildViewController(viewController)
viewController.view.frame = self.bounds
self.addSubview(viewController.view)
viewController.didMove(toParentViewController: parentViewController)
}
}
fileprivate func removeViewController(_ viewController: T?) {
if let _viewController = currentController {
_viewController.willMove(toParentViewController: nil)
_viewController.view.removeFromSuperview()
_viewController.removeFromParentViewController()
}
}
fileprivate func pushViewController(_ controller: T, animated: Bool) {
removeViewController(currentController)
currentController = controller
setUpViewController(controller, animated: false)
}
}
我无法在 UIScrollView
上滚动,因为内容大小设置不正确。有人知道如何解决这个错误吗?
编辑: 你可以在这里看到 git 和一个错误的例子:
GitHub StackViewOnScrollView
您向我们展示了滚动视图和堆栈视图之间的约束。那些看起来不错。这些约束将定义滚动视图的内容大小(假设子项的约束是完全限定的,如下所述)。有关滚动视图约束的详细信息,请参阅 。
问题很可能是 ContainerView
内的约束。鉴于您的错误消息,我怀疑您没有完全限定子视图控制器的约束。
通常在IB中设计场景时,我们不必完全定义垂直约束(因为视图控制器的根视图的高度已为您约束,所以我们关注顶部约束,而不是底部约束约束)。但在这种情况下,由于您要使用子控件的隐式高度,因此您需要完全限定所有约束,实际上相当于以下内容。请注意,不仅有顶部约束,还有底部约束。 (我将在 VFL 中展示,因为这是定义约束的简洁方式,但显然您可以在 IB 中定义这些约束,不一定以编程方式进行。)
V:|-[label]-[stepper]-|
您可能还需要一些东西来指示堆栈视图相对于滚动视图的超级视图的宽度,否则宽度也会不明确(否则它可能会变得非常窄,相对于左边缘).
无论如何,完全约束子视图控制器的视图,会产生如下内容:
我想在 UIStackView
中动态添加视图控制器。
UIStackView
必须是可滚动的,所以我将它添加到 UIScrollView
.
我有一个 UI 错误,内容大小不明确,我尝试使用 Debug View Hierarchy
进行调试,结果如下:
UIStackView
中每个控制器的大小是.zero
--
这是故事板上的层次结构
约束条件:
ScrollView
.leading
=StackView
.leading
ScrollView
.trailing
=StackView
.trailing
ScrollView
.top
=StackView
.top
ScrollView
.bottom
=StackView
.bottom
ScrollView
.width
=StackView
.width
ScrollView
.height
=StackView
.height
我的源代码:
class ScrollViewController: UIViewController {
@IBOutlet weak var scrollView: UIScrollView!
@IBOutlet weak var stackView: UIStackView!
private var strings = ["echo", "hola", "allo"]
private var containerViews = [ContainerView]()
override func viewDidLoad() {
super.viewDidLoad()
self.setupViews()
}
private func setupViews() {
self.setupContainers()
}
fileprivate func removeContainers() {
for container in containerViews {
container.uninstall()
}
containerViews.removeAll()
}
fileprivate func setupContainers() {
removeContainers()
for string in strings {
let viewController = // get the view Controller from StoryBoard
add(viewController)
}
}
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let viewController = segue.destination
let index = strings.index(of: sender as! String)!
}
fileprivate func add(_ viewController: UIViewController) {
let containerView = ContainerView(parentController: self)
containerView.install(viewController)
stackView.addArrangedSubview(containerView)
}
}
class ContainerView<T:UIViewController>: UIView {
unowned var parentViewController: UIViewController
weak var currentController: T?
init(parentController: UIViewController) {
self.parentViewController = parentController
super.init(frame: CGRect.zero)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func install(_ viewController: T) {
pushViewController(viewController, animated: false)
}
func uninstall() {
if let controller = currentController {
removeViewController(controller)
currentController = nil
}
}
fileprivate func setUpViewController(_ targetViewController: T?, animated: Bool) {
if let viewController = targetViewController {
parentViewController.addChildViewController(viewController)
viewController.view.frame = self.bounds
self.addSubview(viewController.view)
viewController.didMove(toParentViewController: parentViewController)
}
}
fileprivate func removeViewController(_ viewController: T?) {
if let _viewController = currentController {
_viewController.willMove(toParentViewController: nil)
_viewController.view.removeFromSuperview()
_viewController.removeFromParentViewController()
}
}
fileprivate func pushViewController(_ controller: T, animated: Bool) {
removeViewController(currentController)
currentController = controller
setUpViewController(controller, animated: false)
}
}
我无法在 UIScrollView
上滚动,因为内容大小设置不正确。有人知道如何解决这个错误吗?
编辑: 你可以在这里看到 git 和一个错误的例子: GitHub StackViewOnScrollView
您向我们展示了滚动视图和堆栈视图之间的约束。那些看起来不错。这些约束将定义滚动视图的内容大小(假设子项的约束是完全限定的,如下所述)。有关滚动视图约束的详细信息,请参阅 。
问题很可能是 ContainerView
内的约束。鉴于您的错误消息,我怀疑您没有完全限定子视图控制器的约束。
通常在IB中设计场景时,我们不必完全定义垂直约束(因为视图控制器的根视图的高度已为您约束,所以我们关注顶部约束,而不是底部约束约束)。但在这种情况下,由于您要使用子控件的隐式高度,因此您需要完全限定所有约束,实际上相当于以下内容。请注意,不仅有顶部约束,还有底部约束。 (我将在 VFL 中展示,因为这是定义约束的简洁方式,但显然您可以在 IB 中定义这些约束,不一定以编程方式进行。)
V:|-[label]-[stepper]-|
您可能还需要一些东西来指示堆栈视图相对于滚动视图的超级视图的宽度,否则宽度也会不明确(否则它可能会变得非常窄,相对于左边缘).
无论如何,完全约束子视图控制器的视图,会产生如下内容: