iosscrollviewstackview

scrollView not working in stackView, How to programmatically scroll a stackView


I added to the ScrollView stackView and added some labels to the stackView, but there is no scroll, why is there no scrolling? I tried adding scrollView in stackView, it didn't work, please, help me, code attached below

class ViewController: UIViewController {

    let scrollView = UIScrollView()

    let formulas = ["", "", "","","", "", "",""]
    
    let formulasStackView: UIStackView = {
        let stackView = UIStackView()
        stackView.backgroundColor = #colorLiteral(red: 0.2587976158, green: 0.2588401437, blue: 0.258788228, alpha: 1)
        stackView.axis = .vertical
        stackView.alignment = .fill
        stackView.distribution = .fillEqually
        stackView.spacing = 5
        return stackView
    }()

    override func viewDidLoad() {
        super.viewDidLoad()
        setupScrollView()
        addFormulas()
    }

    func setupScrollView(){
           scrollView.translatesAutoresizingMaskIntoConstraints = false
            formulasStackView.translatesAutoresizingMaskIntoConstraints = false
           view.addSubview(scrollView)
         
           
           scrollView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
           scrollView.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
           scrollView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
           scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
        
        scrollView.addSubview(formulasStackView)
    
        formulasStackView.centerXAnchor.constraint(equalTo: scrollView.centerXAnchor).isActive = true
        formulasStackView.widthAnchor.constraint(equalTo: scrollView.widthAnchor).isActive = true
        formulasStackView.topAnchor.constraint(equalTo: scrollView.topAnchor).isActive = true
        
       }

    func addFormulas() {
        for index in 0..<formulas.count {
            addFormulaView()
        }
    }

    func addFormulaView() {
        let formulaView: UIView = {
            let view = UIView()
            view.backgroundColor = #colorLiteral(red: 0.1882152259, green: 0.1882481873, blue: 0.1882079244, alpha: 1)
            view.layer.cornerRadius = 6
            view.translatesAutoresizingMaskIntoConstraints = false
            return view
        }()
        formulasStackView.addArrangedSubview(formulaView)
        
        formulaView.heightAnchor.constraint(equalToConstant: 200).isActive = true
        formulaView.leadingAnchor.constraint(equalTo: formulasStackView.leadingAnchor, constant: 5).isActive = true
        formulaView.trailingAnchor.constraint(equalTo: formulasStackView.trailingAnchor, constant: -5).isActive = true
        formulaView.translatesAutoresizingMaskIntoConstraints = false
    }
}

Solution

  • Couple things...

    First, not directly related, but it's a good idea to constrain elements to the view's Safe Area.

    Second, we want to constrain positioning (Top / Leading / Trailing / Bottom) of the scroll view's subviews to the scroll view's Content Layout Guide (in this case, the only subview will be your stack view -- your "formula views" will be subviews of the stack view). Constrain sizing (in this case, the stack view's width) to the scroll view's Frame Layout Guide.

    Third, if a UI element is the width of its superView, it's better practice to use Leading and Trailing constraints, rather than Width and CenterX constraints.

    So, here's your class with some changes -- review the comments in the code:

    class ViewController: UIViewController {
        
        let scrollView = UIScrollView()
        
        let formulas = ["", "", "","","", "", "",""]
        
        let formulasStackView: UIStackView = {
            let stackView = UIStackView()
            stackView.backgroundColor = #colorLiteral(red: 0.2587976158, green: 0.2588401437, blue: 0.258788228, alpha: 1)
            stackView.axis = .vertical
            stackView.alignment = .fill
            stackView.distribution = .fillEqually
            stackView.spacing = 5
            return stackView
        }()
        
        override func viewDidLoad() {
            super.viewDidLoad()
            setupScrollView()
            addFormulas()
        }
        
        func setupScrollView(){
            scrollView.translatesAutoresizingMaskIntoConstraints = false
            formulasStackView.translatesAutoresizingMaskIntoConstraints = false
            view.addSubview(scrollView)
            
            // always a good idea to respect the safe-area
            
            let g = view.safeAreaLayoutGuide
            
            NSLayoutConstraint.activate([
    
                scrollView.topAnchor.constraint(equalTo: g.topAnchor, constant: 0.0),
                scrollView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 0.0),
                scrollView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: 0.0),
                scrollView.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: 0.0),
    
                // use Leading and Trailing anchors instead
                //scrollView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
                //scrollView.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
                
            ])
            
            scrollView.addSubview(formulasStackView)
            
            let cg = scrollView.contentLayoutGuide
            let fg = scrollView.frameLayoutGuide
            
            NSLayoutConstraint.activate([
                
                // let's "inset" the stack view with 5-pts "padding" on all four sides
                formulasStackView.topAnchor.constraint(equalTo: cg.topAnchor, constant: 5.0),
                formulasStackView.leadingAnchor.constraint(equalTo: cg.leadingAnchor, constant: 5.0),
                formulasStackView.trailingAnchor.constraint(equalTo: cg.trailingAnchor, constant: -5.0),
                formulasStackView.bottomAnchor.constraint(equalTo: cg.bottomAnchor, constant: -5.0),
                
                // we have 5-pts on each side of the stack view, so
                //  constrain its width minus 10-pts
                formulasStackView.widthAnchor.constraint(equalTo: fg.widthAnchor, constant: -10.0),
            ])
            
            // these are wrong
            //formulasStackView.centerXAnchor.constraint(equalTo: scrollView.contentLayoutGuide.centerXAnchor).isActive = true
            //formulasStackView.widthAnchor.constraint(equalTo: scrollView.widthAnchor).isActive = true
            //formulasStackView.topAnchor.constraint(equalTo: scrollView.topAnchor).isActive = true
            
        }
        
        func addFormulas() {
            for index in 0..<formulas.count {
                addFormulaView()
            }
        }
        
        func addFormulaView() {
            let formulaView: UIView = {
                let view = UIView()
                view.backgroundColor = #colorLiteral(red: 0.1882152259, green: 0.1882481873, blue: 0.1882079244, alpha: 1)
                view.layer.cornerRadius = 6
                view.translatesAutoresizingMaskIntoConstraints = false
                return view
            }()
            formulasStackView.addArrangedSubview(formulaView)
    
            // views added as arranged subviews automatically get this set to false
            //  so not necessary
            //formulaView.translatesAutoresizingMaskIntoConstraints = false
    
            // all we need to do is constrain the height
            formulaView.heightAnchor.constraint(equalToConstant: 200).isActive = true
            
            // do NOT add either of these constraints
            //formulaView.leadingAnchor.constraint(equalTo: formulasStackView.leadingAnchor, constant: 5).isActive = true
            //formulaView.trailingAnchor.constraint(equalTo: formulasStackView.trailingAnchor, constant: -5).isActive = true
        }
    }