Search code examples
swiftuiscrollviewuikituistackviewsnapkit

Programmatically UIView with error Scrollable content size is ambiguous for UIScrollView. Only the first time


I am experiencing an odd behaviour with my UIViewController which contains a UIScrollView. The ViewController is pushed from another ViewController. The layout for the view is following

  • UIView
    • UIScrollView
      • UIView
        • UIImageVIew
          • UILabel
        • UIStackView

The way I set up my constraints and update the stackView content is the following.

private extension PresentationViewController {

    func setupView() {
        isScrolling = true

        view.addSubview(scrollView)
        scrollView.snp.makeConstraints { (builder) in
            builder.top.left.bottom.right.equalToSuperview()
        }

        scrollView.addSubview(contenView)
        contenView.snp.makeConstraints { (builder) in
            builder.top.left.bottom.right.equalToSuperview()
            builder.width.equalToSuperview()
        }

        contenView.addSubview(headerImageView)
        headerImageView.snp.makeConstraints { (builder) in
            builder.top.left.right.equalToSuperview()
            builder.height.equalTo(view.frame.height / 2)
        }

        headerImageView.addSubview(headerTitleLabel)
        headerTitleLabel.snp.makeConstraints { (builder) in
            builder.left.right.equalToSuperview()
            builder.centerX.equalToSuperview()
            builder.centerY.equalToSuperview()
        }

        contenView.addSubview(presentationStackView)
        presentationStackView.snp.makeConstraints { (builder) in
            builder.top.equalTo(headerImageView.snp.bottom).inset(-Margins.xxLarge)
            builder.left.right.equalToSuperview()
            builder.bottom.equalTo(scrollView.snp.bottom)
        }

    }

    func updateView(presentationResponse: PresentationResponse?) {
        guard let presentationResponse = presentationResponse else { return }

        let firstPresentation = presentationResponse.presentationItems[0]
        titleView.title = presentationResponse.galleryName

        headerImageView.sd_setImage(with: URL(string: firstPresentation.imageSet.fullSize ?? ""), placeholderImage: nil)
        headerTitleLabel.text = presentationResponse.galleryName

        for item in presentationResponse.presentationItems.dropFirst() {

            let sectionImageView = UIImageView()
            sectionImageView.contentMode = .scaleAspectFit
            sectionImageView.clipsToBounds = true

            let sectionCaptionLabel = BaseLabel(withConfiguration: .headline)
            sectionCaptionLabel.text = item.rowCaption

            let sectionView = UIView()
            sectionView.addSubview(sectionImageView)
            sectionView.addSubview(sectionCaptionLabel)

            sectionImageView.sd_setImage(with: URL(string: item.imageSet.defaultImage ?? "")) { [weak self, weak sectionView] (image, error, _, _) in

                guard let strongSelf = self,
                    let sectionView = sectionView,
                    let image = image else { return }

                strongSelf.presentationStackView.addArrangedSubview(sectionView)

                sectionImageView.snp.makeConstraints { (builder) in
                    builder.top.left.bottom.equalToSuperview().inset(Margins.medium)
                    let width:CGFloat = strongSelf.view.frame.size.width / 3
                    builder.width.equalTo(width)
                    builder.height.equalTo(width * (1 / image.aspectRatioValue))

                }

                sectionCaptionLabel.snp.makeConstraints { (builder) in
                    builder.left.equalTo(sectionImageView.snp.right).inset(-Margins.medium)
                    builder.top.right.equalToSuperview().inset(Margins.medium)
                }

            }

        }

    }

}

The setupView() is triggered in ViewDidLoad and updateView(:) when an async action to the backend has been completed.

As you might have seen, I am using Snapkit for my constraints. When inspecting my UI with the debugger tools i get the message:

Scrollable content size is ambiguous for UIScrollView

The issue seems to be related with the UIStackView because it's not showing. However, if I go back and push again I don't have the error and everything shows correctly.


Solution

  • If the layout displays correctly, you don't really need to worry about the

    Scrollable content size is ambiguous for UIScrollView
    

    warning in debug view hierarchy. However, if you want to get rid of it, add a line in your stack view setup:

        presentationStackView.snp.makeConstraints { (builder) in
            builder.top.equalTo(headerImageView.snp.bottom).inset(-Margins.xxLarge)
            builder.left.right.equalToSuperview()
            builder.bottom.equalTo(scrollView.snp.bottom)
    
            // add this line
            builder.height.equalTo(0).priority(250)
    
        }
    

    That will give the stack view a valid height (of Zero) for auto-layout to consider when it is empty... but the low priority of 250 will allow it to expand vertically as you add arranged subviews.