Home > Net >  UIScrollView not showing inside UIStackView
UIScrollView not showing inside UIStackView

Time:06-29

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.

CodePudding user response:

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.

CodePudding user response:

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
  • Related