Search code examples
iosswiftuiscrollviewuistackview

Why can't I see UIStackView in UIScrollView?


I'm going to put UIStackView in UIScrollView. To put it easier, I'm going to make a mind map. The UIStackView I put into UIScrollView is an object of mind map. Thus, The embedded UIStackView will appear in all zones of UIScrollView.

However, although I have specified the location of UIStackView for UIScrollView, UIStackView does not appear in UIScrollView.

My interface builder(Attaches an interface builder image to understand the My View hierarchy.):

enter image description here

import UIKit

class ViewController: UIViewController {
    @IBOutlet weak var scrollView: UIScrollView!
    @IBOutlet weak var createObject: UIBarButtonItem!

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        let redView = UIView()
        redView.backgroundColor = .red

        let blueView = UIView()
        blueView.backgroundColor = .blue

        let stackView = UIStackView(arrangedSubviews: [redView, blueView])
        stackView.axis = .vertical
        stackView.distribution = .fillEqually

        stackView.center = scrollView.center

        scrollView.addSubview(stackView)
    }
}

Solution

  • Neither UIView nor UIStackView have intrinsic sizes.

    So you end up adding a stack view of size 0, 0 containing two views, each sized 0, 0, to your scroll view.

    You need to apply some sizing somewhere.

    This will give you a stack view that is equal width to your scroll view, and twice as tall (so you have a "full-height" blue view and a a "full-height" red view):

    class ViewController: UIViewController {
    
        @IBOutlet weak var scrollView: UIScrollView!
    
        override func viewDidLoad() {
            super.viewDidLoad()
            // Do any additional setup after loading the view.
            let redView = UIView()
            redView.backgroundColor = .red
    
            let blueView = UIView()
            blueView.backgroundColor = .blue
    
            let stackView = UIStackView(arrangedSubviews: [redView, blueView])
            stackView.axis = .vertical
            stackView.distribution = .fillEqually
    
            scrollView.addSubview(stackView)
    
            stackView.translatesAutoresizingMaskIntoConstraints = false
    
            NSLayoutConstraint.activate([
                stackView.topAnchor.constraint(equalTo: scrollView.topAnchor, constant: 0.0),
                stackView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor, constant: 0.0),
                stackView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor, constant: 0.0),
                stackView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor, constant: 0.0),
    
                stackView.widthAnchor.constraint(equalTo: scrollView.widthAnchor, constant: 0.0),
                stackView.heightAnchor.constraint(equalTo: scrollView.heightAnchor, multiplier: 2.0),
                ])
    
        }
    }
    

    Edit:

    Here is another example. It adds 6 subviews, with specified widths and heights. The stack view properties are changed to:

    .alignment = .center
    .distribution = .fill
    .spacing = 8
    

    The result:

    enter image description here

    and scrolled down:

    enter image description here

    and the code:

    class ViewController: UIViewController {
    
        @IBOutlet weak var scrollView: UIScrollView!
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            let colors: [UIColor] = [
                .red,
                .blue,
                .green,
                .orange,
                .yellow,
                .cyan,
            ]
    
            let sizes: [CGSize] = [
                CGSize(width: 100, height: 150),
                CGSize(width: 250, height: 100),
                CGSize(width: 200, height: 120),
                CGSize(width: 160, height: 200),
                CGSize(width: 220, height: 180),
                CGSize(width:  60, height:  80),
            ]
    
            let stackView = UIStackView()
            stackView.axis = .vertical
            stackView.distribution = .fill
            stackView.alignment = .center
            stackView.spacing = 8
    
            for (color, size) in zip(colors, sizes) {
                let v = UIView()
                v.translatesAutoresizingMaskIntoConstraints = false
                v.widthAnchor.constraint(equalToConstant: size.width).isActive = true
                v.heightAnchor.constraint(equalToConstant: size.height).isActive = true
                v.backgroundColor = color
                stackView.addArrangedSubview(v)
            }
    
            scrollView.addSubview(stackView)
    
            stackView.translatesAutoresizingMaskIntoConstraints = false
    
            NSLayoutConstraint.activate([
                stackView.topAnchor.constraint(equalTo: scrollView.topAnchor, constant: 0.0),
                stackView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor, constant: 0.0),
                stackView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor, constant: 0.0),
                stackView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor, constant: 0.0),
    
                stackView.widthAnchor.constraint(equalTo: scrollView.widthAnchor, constant: 0.0),
    
                // do NOT set a heightAnchor ... let the subviews define the height
    //          stackView.heightAnchor.constraint(equalTo: scrollView.heightAnchor, multiplier: 2.0),
                ])
    
        }
    }