Andrei
Andrei

Reputation: 71

Changing Size Classes

I’ve been trying to get up and running with building adaptive layouts in pure code (no storyboards used). I use layout anchors to set constraints and utilize traitCollectionDidChange method to vary between various sets of constraints and other interface changes. I use a switch statement to call the corresponding method with the corresponding set of constraints.

override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
    super.traitCollectionDidChange(previousTraitCollection)

    switch (traitCollection.horizontalSizeClass, traitCollection.verticalSizeClass) {
    case (.regular, .regular):
        setupRegularRegular()

    case (.compact, .compact):
        setupCompactCompact()

    case (.regular, .compact):
        setupRegularCompact()

    case (.compact, .regular):
        setupCompactRegular()

    default: break
    }

}

For the sake of testing, I changed only one constraint, which is centerYAnchor.constraint. In portrait the constant is -150, in landscape I want it to be changed to 50. For iPad, I set the constant to 0.

bigLabel.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: -150).isActive = true

It works fine when switching between iPhone and iPad. However, if you start rotating the iPhone from portrait to landscape, the layout system fails, saying:

[LayoutConstraints] Unable to simultaneously satisfy constraints.
Probably at least one of the constraints in the following list is one you don't want. 
Try this: 
    (1) look at each constraint and try to figure out which you don't expect; 
    (2) find the code that added the unwanted constraint or constraints and fix it. 
(
"<NSLayoutConstraint:0x600000097890 UILabel:0x7fac02c08aa0'Main Label'.centerY == UIView:0x7fac02e0a950.centerY - 150   (active)>",
"<NSLayoutConstraint:0x604000097840 UILabel:0x7fac02c08aa0'Main Label'.centerY == UIView:0x7fac02e0a950.centerY + 50   (active)>"
)

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0x604000097840 UILabel:0x7fac02c08aa0'Main Label'.centerY == UIView:0x7fac02e0a950.centerY + 50   (active)>

I tried overriding various methods and setting the isActive property of the constraint to false and then back to true, but that didn’t help. I searched the web for a couple of days. No solution so far. So my question is, how do you actually switch between size classes the right way when rotating the device? Is it necessary to override any other method or something? Is there a better solution for adaptive layout in pure code? Are there any best practices when it comes to adaptive layouts / size classes / layout anchors and constraints in code (without using the storyboard)? Thanks for help!

P.S. the methods setting up constraints are:

func setupCompactRegular() {

    bigLabel.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: -150).isActive = true

}

func setupRegularCompact() {

    bigLabel.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: 50).isActive = true

}

Upvotes: 4

Views: 4163

Answers (1)

Marcio Romero Patrnogic
Marcio Romero Patrnogic

Reputation: 1156

the issue is you are creating new constraints.. try holding a reference to your constraint and change your methods to update them

func setupCompactRegular() {

    self.bigLabelCenterYAnchor.constant = -150

}

func setupRegularCompact() {

    self.bigLabelCenterYAnchor.constant = 50

}

Call layoutIfNeeded() right after if it doesnt update automatically

Upvotes: 5

Related Questions