swiftuiviewuiviewcontrollerstackview

creating stackView inside of view controller crashes with 'Unable to activate constraint with anchors'


I am trying to create a very simple stackView in my viewcontroller. I want the stackView to cover the entire screen. How I am creating the stackView is

import UIKit
import Firebase

class Vc: UIViewController {
        
var scrollView: UIScrollView {
    let scroll = UIScrollView()
    scroll.isScrollEnabled = true
    return scroll
}

var stackView: UIStackView {
    let stack = UIStackView()
    stack.axis = .vertical
    stack.distribution = .fillEqually
    return stack
    
}
    

override func viewDidLoad() {
    super.viewDidLoad()
    navigationController?.navigationBar.tintColor = .white
    self.navigationItem.title = "Profile"
    self.view.backgroundColor = UIColor.white
    addStackViewAnchors()
    
}

func addStackViewAnchors() {
    view.addSubview(stackView)
    stackView.anchors(top: view.safeAreaLayoutGuide.topAnchor, topPad: 0, bottom: view.safeAreaLayoutGuide.bottomAnchor, bottomPad: 0, left: view.safeAreaLayoutGuide.leftAnchor, leftPad: 0, right: view.safeAreaLayoutGuide.rightAnchor, rightPad: 0, height: .zero, width: .zero)
}

}

When I run this, my app crashes and it says

Terminating app due to uncaught exception 'NSGenericException', reason: 'Unable to activate constraint with anchors <NSLayoutYAxisAnchor:0x283c5e840 "UIStackView:0x135666a90.top"> and <NSLayoutYAxisAnchor:0x283c5e140 "UILayoutGuide:0x28103b8e0'UIViewSafeAreaLayoutGuide'.top"> because they have no common ancestor.  Does the constraint or its anchors reference items in different view hierarchies?  That's illegal.'
 terminating with uncaught exception of type NSException

There are no conflicting constraints because the stackView is the only anchor being set.


Solution

  • Every time you called stackView here you init a new instance of Stack View from inside your function. That's the reason when you called stackView.anchor the error appears because it is autolayout the new instance of stackView not the stackView you add subview into your VC.

    The thing you should do is create a variable to store your stackView instance so the stackView init for once and reuse in all in the VC.

    Beside of that, we use leadingAnchor instead of leftAnchor; trailingAnchor instead of rightAnchor.

    Code will be like this

    class ViewController: UIViewController {
        private var stackViewInScreen : UIStackView?
        
        var stackView: UIStackView {
            let stack = UIStackView()
            stack.axis = .vertical
            stack.distribution = .fillEqually
            return stack
        }
        
        override func viewDidLoad() {
            super.viewDidLoad()
            self.view.backgroundColor = UIColor.white
            addStackViewAnchors()
        }
    
        func addStackViewAnchors() {
            self.stackViewInScreen = stackView
            self.view.addSubview(self.stackViewInScreen!)
            
            self.stackViewInScreen!.translatesAutoresizingMaskIntoConstraints = false
            self.stackViewInScreen?.backgroundColor = UIColor.orange
            NSLayoutConstraint.activate([
                self.stackViewInScreen!.topAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.topAnchor, constant: 0),
                self.stackViewInScreen!.leadingAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.leadingAnchor, constant: 0),
                self.stackViewInScreen!.trailingAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.trailingAnchor, constant: 0),
                self.stackViewInScreen!.bottomAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.bottomAnchor, constant: 0)
            ])
        }
    }