iosswiftuitabbarcontrollernslayoutconstraintiboutlet

View Controller loses connection to constraints in IBOutlets when switching tabs in UITabView


OK, so here's the relevant bit of my code:

class MyViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
    @IBOutlet weak var questionView: QuestionView!
    @IBOutlet weak var answerView: AnswerView!
    
    @IBOutlet weak var tableView: UITableView!
    
    @IBOutlet weak var tableViewTopConstraint: NSLayoutConstraint! // Active at start, attaches tableView.topAnchor to questionView.bottomAnchor
    @IBOutlet weak var tableViewTopAlternateConstraint: NSLayoutConstraint! // Inactive at start, attaches tableView.topAnchor to answerView.bottomAnchor
    
    func submitAnswer() {
        tableViewTopConstraint.isActive = false
        tableViewTopAlternateConstraint = true
    }
    
    func newQuestion() {
        tableViewTopConstraint.isActive = true
        tableViewTopAlternateConstraint = false
    }
}

I'm building a question/answer type of app and the two subviews are different sizes for design reasons. I have it set so that it toggles between the two subviews depending on the current state (toggling back for a new question when the user taps to advance), and it works fine...

...until I go to a different tab on the UITabView that encloses everything (e.g. to change the settings then resume testing)

The moment the tab changes it's like the NSLayoutConstraint outlets no longer exist. The orders to change them still process (I've verified this in the console), but they do nothing.

Any suggestions on how else to attack this problem?


Solution

  • First, don't set @IBOutlet properties to weak. I know that's the default, but it's not correct.

    If you have this:

    @IBOutlet weak var tableViewTopAlternateConstraint: NSLayoutConstraint!
    

    connected to a constraint via Storyboard, and you do this:

    tableViewTopAlternateConstraint.isActive = false
    

    you just removed that constraint from existence.

    In addition, if you have two different Top constraints in Storyboard, you should be getting an Error indicator as they cannot be satisfied.

    Better to either change the Priorities, or use a single constraint and change the .constant value.

    So, two constraints in Storyboard:

    and your code becomes:

    func submitAnswer() {
        tableViewTopConstraint.priority = .defaultLow
        tableViewTopAlternateConstraint.priority = .defaultHigh
    }
    
    func newQuestion() {
        tableViewTopAlternateConstraint.priority = .defaultLow
        tableViewTopConstraint.priority = .defaultHigh
    }