skip to Main Content

I have one UIStackView that stacks vertically two horizontal UIStackViews. That worked just fine. But the first sub-UIStackView had too many elements so I decided to wrap it inside a scrollView. The problem is that when I do so, this first sub-view just disappears, as if the UIScrollView had a height of 0.

Here’s the code:

func initViews(){
    let containerStack = UIStackView()
    addSubview(containerStack)
    containerStack.axis = .vertical
    containerStack.distribution = .equalCentering
    containerStack.isLayoutMarginsRelativeArrangement = true
    containerStack.layoutMargins = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
    
    
    let scrollView = UIScrollView()
    let scrolledStack = UIStackView()
    containerStack.addArrangedSubview(scrollView)
    scrollView.addSubview(scrolledStack)
    scrolledStack.distribution = .fill
    scrolledStack.isLayoutMarginsRelativeArrangement = true
    scrolledStack.layoutMargins = UIEdgeInsets(top: 20, left: 30, bottom: 20, right: 30)
    
    scrolledStack.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor).isActive = true
    scrolledStack.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor).isActive = true
    scrolledStack.topAnchor.constraint(equalTo: scrollView.topAnchor).isActive = true
    scrolledStack.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor).isActive = true
    scrolledStack.widthAnchor.constraint(equalTo: scrollView.widthAnchor).isActive = true
    
    
    let otherStack = UIStackView()
    containerStack.addArrangedSubview(otherStack)
    otherStack.distribution = .equalCentering
    otherStack.isLayoutMarginsRelativeArrangement = true
    otherStack.layoutMargins = UIEdgeInsets(top: 20, left: 30, bottom: 0, right: 30)
    
    
    containerStack.translatesAutoresizingMaskIntoConstraints = false
    containerStack.topAnchor.constraint(equalTo: topAnchor).isActive = true
    containerStack.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
    containerStack.leftAnchor.constraint(equalTo: leftAnchor).isActive = true
    containerStack.rightAnchor.constraint(equalTo: rightAnchor).isActive = true
    
    
    for number in ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"]{
        let text = UITextView()
        text.font = UIFont.boldSystemFont(ofSize: 14)
        text.text = number
        text.isScrollEnabled = false
        text.backgroundColor = .clear
        
        scrolledStack.addArrangedSubview(text)
    }
    for number in ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"]{
        let text = UITextView()
        text.font = UIFont.boldSystemFont(ofSize: 14)
        text.text = number
        text.isScrollEnabled = false
        text.backgroundColor = .clear
        
        otherStack.addArrangedSubview(text)
    }
}

otherStack behaves as expected, but scrollView and scrolledStack just don’t appear. If I remove the scrollView and just add scrolledStack to containerStack, both stacks appear as expected.

2

Answers


  1. Many UI view elements – such as UIScrollView – have no intrinsic height.

    You are putting a scroll view in a vertical stack view, with your stack view set to:

    containerStack.distribution = .equalCentering
    

    The problem comes in because the stack view cannot center its arranged subviews if they have no height.

    If you change your containerStack to:

    containerStack.distribution = .fillEqually
    

    You will see both of the arranged subviews.

    If you don’t want them to have equal heights, then you need to provide some height value… either with an explicit scrollView.heightAnchor.constraint(equalToConstant: 100.0).isActive = true or perhaps relative to the overall height.


    Edit – in response to comment…

    Add these lines at the bottom of your initViews() func:

    scrolledStack.translatesAutoresizingMaskIntoConstraints = false
    
    scrollView.backgroundColor = .red
    scrolledStack.backgroundColor = .lightGray
    otherStack.backgroundColor = .systemBlue
    containerStack.backgroundColor = .systemGreen
    

    and, instead of .clear, give your first set of text views:

    text.backgroundColor = .cyan
    

    and your second set of text views:

    text.backgroundColor = .green
    

    Here’s how it looks now when I run your code:

    enter image description here

    That is probably not what you’re going for, but by giving your views contrasting background colors, it makes it much easier to see what’s going on.

    Login or Signup to reply.
  2. I readapted a bit your code and created this swift playground:

    import UIKit
    import PlaygroundSupport
    
    final class ViewController: UIViewController {
    
      override func viewDidLoad() {
        super.viewDidLoad()
    
        let containerStack = UIStackView()
        view.addSubview(containerStack)
        containerStack.axis = .vertical
        containerStack.distribution = .equalCentering
        containerStack.translatesAutoresizingMaskIntoConstraints = false
    
        let scrollView = UIScrollView()
        let scrolledStack = UIStackView()
        scrolledStack.translatesAutoresizingMaskIntoConstraints = false
        containerStack.addArrangedSubview(scrollView)
        scrollView.addSubview(scrolledStack)
    
        NSLayoutConstraint.activate([
          scrolledStack.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor),
          scrolledStack.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor),
          scrolledStack.topAnchor.constraint(equalTo: scrollView.topAnchor),
          scrolledStack.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor),
          scrolledStack.centerYAnchor.constraint(equalTo: scrollView.centerYAnchor)
        ])
    
        let otherStack = UIStackView()
        containerStack.addArrangedSubview(otherStack)
        otherStack.distribution = .equalCentering
        otherStack.isLayoutMarginsRelativeArrangement = true
        otherStack.layoutMargins = UIEdgeInsets(top: 20, left: 30, bottom: 0, right: 30)
    
        NSLayoutConstraint.activate([
          containerStack.topAnchor.constraint(equalTo: view.topAnchor),
          containerStack.bottomAnchor.constraint(equalTo: view.bottomAnchor),
          containerStack.leftAnchor.constraint(equalTo: view.leftAnchor),
          containerStack.rightAnchor.constraint(equalTo: view.rightAnchor),
        ])
    
        for number in ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"]{
          let text = UILabel()
          text.font = UIFont.boldSystemFont(ofSize: 14)
          text.text = number
    
          scrolledStack.addArrangedSubview(text)
          NSLayoutConstraint.activate([
            text.widthAnchor.constraint(equalToConstant: 100)
          ])
        }
        for number in ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"]{
          let text = UILabel()
          text.font = UIFont.boldSystemFont(ofSize: 14)
          text.text = number
    
          otherStack.addArrangedSubview(text)
        }
    
      }
    }
    
    PlaygroundPage.current.liveView = ViewController()
    PlaygroundPage.current.needsIndefiniteExecution = true
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search