Search code examples
swiftswift3constraintsnslayoutconstraint

Adding constraints programmatically makes crash


I would like to add programmatically few constraints while I slide from a bottom new table view, but so far I can not understand what am I doing wrong. In my super view I have map view which have to change bottom constraint to make a space for new tableView while the button is clicked.

Basically for my table view I need 4 constraints:

  • top constraint to the map bottom
  • bottom constraint to the tab bar top constraint
  • leading and trailing from super view

All the time I am getting crash:

Impossible to set up layout with view hierarchy unprepared for constraint.

Based on setting constraints from: here

Here is a code:

@IBAction func test(_ sender: UIButton) {
        let tableView = UITableView()
        tableView.backgroundColor = UIColor.red
        tableView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(tableView)
        
        let topConstraint = NSLayoutConstraint(item: tableView, attribute: .top, relatedBy: .equal, toItem: self.mapView, attribute: .bottom, multiplier: 1, constant: 0)
        let bottomConstraint = NSLayoutConstraint(item: tableView, attribute: .bottom, relatedBy: .equal, toItem: self.tabBarController?.tabBar, attribute: .top, multiplier: 1, constant: 0)
        let leadingConstraint = NSLayoutConstraint(item: tableView, attribute: .leading, relatedBy: .equal, toItem: self.view, attribute: .leading, multiplier: 1, constant: 0)
        let trailingConstraint = NSLayoutConstraint(item: tableView, attribute: .trailing, relatedBy: .equal, toItem: self.view, attribute: .trailing, multiplier: 1, constant: 0)
        
        view.addConstraints([topConstraint, bottomConstraint, leadingConstraint, trailingConstraint])
//        tableView.addConstraint(NSLayoutConstraint(item: tableView, attribute: .top, relatedBy: .equal, toItem: self.mapView, attribute: .bottom, multiplier: 1, constant: 0))
//        tableView.addConstraint(NSLayoutConstraint(item: tableView, attribute: .bottom, relatedBy: .equal, toItem: self.tabBarController?.tabBar, attribute: .top, multiplier: 1, constant: 0))
//        tableView.addConstraint(NSLayoutConstraint(item: tableView, attribute: .leading, relatedBy: .equal, toItem: self.view, attribute: .leading, multiplier: 1, constant: 0))
//        tableView.addConstraint(NSLayoutConstraint(item: tableView, attribute: .trailing, relatedBy: .equal, toItem: self.view, attribute: .trailing, multiplier: 1, constant: 0))
        
        self.view.layoutIfNeeded()
        UIView.animate(withDuration: 0.5, animations: {
            self.mapBottomConstaint.constant = 200
            self.centerButtonBottomConstaint.constant = 208
            self.view.layoutIfNeeded()
        })
    }

Solution

  • You are trying to set a constraint to a view that is outside your view hierarchy. Auto Layout system provides layout guides to help you align your views with navigation bars and tab bars.

    In your bottom constraint, replace self.tabBarController?.tabBar with bottomLayoutGuide.

    As a side note, new layout syntax (available on iOS 9+) makes much easier creating programmatic constraints without third party libraries:

    let topConstraint = tableView.topAnchor.constraint(equalTo: mapView.bottomAnchor)
    let bottomConstraint = tableView.bottomAnchor.constraint(equalTo: bottomLayoutGuide.topAnchor)
    let leadingConstraint = tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor)
    let trailingConstraint = tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor)
    

    More info for creating programmatic constraints here.