4

I would like to add dynamically View Controllers inside a UIStackView. The UIStackView must be scrollable so I added it inside a UIScrollView.

I have an UI error, the content size is ambiguous, I tried to debug using Debug View Hierarchy and here it's the result :

Bug Content Size is ambiguous UIScrollView UIStackView

Sizes of each controller inside the UIStackView is .zero

Debug View Hierarchy - UIScrollView UIStackView

--

Here is my hierarchy on the StoryBoard

Architecture StackView inside ScrollView

UIStackView inside UIScrollView

The constraints :

  • ScrollView.leading = StackView.leading
  • ScrollView.trailing = StackView.trailing
  • ScrollView.top = StackView.top
  • ScrollView.bottom = StackView.bottom
  • ScrollView.width = StackView.width
  • ScrollView.height = StackView.height

My source code :

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)
    }

}

I cannot scroll on the UIScrollView because the content size is not set correctly. Anyone know how to resolve this bug ?

EDIT: You can see here the git with an example of the bug : GitHub StackViewOnScrollView

Kevin Machado
  • 4,141
  • 3
  • 29
  • 54
  • I want the subviews to be sized as its intrinsic content. In my case, the constraints are well set on each controller, but the `contentSize` is not correct on the `UIScrollView` – Kevin Machado Oct 31 '16 at 17:04

1 Answers1

2

You're showing us the constraints between the scrollview and the stack view. Those look fine. These constraints will define the content size of the scroll view (assuming the child's constraints are fully qualified, as discussed below). For specifics of constraints with scroll views, see https://stackoverflow.com/a/16843937/1271826.

The problem is likely the constraints within the ContainerView. I suspect that, given your error message, that you have not fully qualified the constraints of the child view controller.

Often when designing scenes in IB, we don't have to fully define the vertical constraints (because the height of the view controller's root view is constrained for you, so we focus on the top constraints, but not the bottom constraints). But in this case, since you are going to use the implicit height of the child's controls, you need to fully qualify all of the constraints, effectively something equivalent to the following. Note, there are not only the top constraint, but a bottom constraint, too. (I'll show in VFL because that's a concise way to define the constraints, but clearly you can define these in IB, not necessarily programmatically.)

V:|-[label]-[stepper]-|

You also probably want something that dictates the width of the stack view relative to the scroll view's superview, too, otherwise the width will be ambiguous, too (and it otherwise will probably make it really narrow, against the left edge).

Anyway, fully constraining the child view controller's views, that yields something like:

screen snapshot

Community
  • 1
  • 1
Rob
  • 415,655
  • 72
  • 787
  • 1,044
  • I already have all the constraints set, I also set `UILabel` and `UIStepper` with `Content Compression Resistance Vertical` = 1000 – Kevin Machado Oct 31 '16 at 19:30
  • I published the example, same hierarchy, classes.. same bug :) https://github.com/djnivek/StackViewOnScrollView – Kevin Machado Oct 31 '16 at 20:01
  • 1
    @thedjnivek - I spent a little time trying to figure out what was going on there and was confused by (a) instantiating all of these child view controllers with an empty segue; and (b) manually instantiating that `ContainerView`, which wasn't tied to any view in the storyboard. It was all very confusing, so I cut the Gordian knot, and simplified it, and it now works. See https://github.com/robertmryan/StackViewOnScrollView. – Rob Oct 31 '16 at 21:27
  • Bottom line, though, the problem was _not_ the constraints represented in the storyboard, but likely either (a) how you were instantiating that scene; or (b) some disconnect between the view hierarchy and the view controller hierarchy. – Rob Oct 31 '16 at 21:30
  • That's perfect !! it's working ! ;) I think it was disconnected from the view hierarchy ! Thanks ! – Kevin Machado Oct 31 '16 at 22:49
  • Hi @Rob and thanks for your answer. This post is quite old but almost matches what I'm trying to do. I downloaded your sample code, Created a CollectionViewController that I try to add as the last subview of my stack view, using exactly the same code as yours. Unfortunately, the collection view controller won't appear, while I can see it while scrolling but it would disappear as soon as I stop touching the screen. Do you think it's possible to add a CollectionViewController as a child of this view hierarchy? – H4Hugo Dec 27 '18 at 08:36