I have a, relatively, simple layout, which is divided into two main sections, which I've encompassed with two UIViews to make it easier to manage.
My goal was to provide two different layouts, one for portrait and one for landscape.
My first idea was to make use of "Vary for Traits" for both "width" and "height", so, I set the storyboard in portrait mode, clicked on "Vary for Traits", I applied the traits I wanted in portrait mode, clicked "Done", switched to landscape mode and repeated the process.
As you can see, switching between the orientations in the storyboard works just fine
Except, when I run it on a device ... it doesn't work ... as expected
Okay , so I thought, I go back and do it again, but this time just vary for the "width" and yet again, it doesn't work.
So, I then thought, I'd go old school. I'd add each constraint and set a variant for the specific size class...
(I have different constraints for both orientations, so a single constraint doesn't have a variant for both)
And ... again, this doesn't work. Before some tells me that "Installed" needs to be ticked for both orientations, no, it doesn't, that just makes Xcode angry and spit lots of nasty comments in the console.
The "question"
So, the "simple" question is, what am I doing wrong? Have I missed something bleedingly obvious which would make this work, or is having a separate constraint for both portrait and landscape just a really dumb idea (which I though what "Vary for Traits" was basically doing)
A "hacky" workaround
So, after spending WAY to much time on this, I fell back to an old "workflow" I've used in the past for manually created UIs
Starting from the point of the manually created constraints for each variant, I essentially, placed all of the landscape and portrait constraints into individual IBOutlet collections and when viewWillTransition was called, manually de/activated the required group of constraints...
class ViewController: UIViewController {
@IBOutlet var landscapeConstraints: [NSLayoutConstraint]!
@IBOutlet var portraitConstraints: [NSLayoutConstraint]!
override func viewDidLoad() {
super.viewDidLoad()
}
enum Orientation {
case landscape
case portrait
case unknown
}
fileprivate var lastOrientation: Orientation = .unknown
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
updateCurrentConstraints(toSize: view.bounds.size)
}
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
coordinator.animate { (ctx) in
self.updateCurrentConstraints(toSize: size)
} completion: { (ctx) in
}
}
func updateCurrentConstraints(toSize size: CGSize) {
var orientation: Orientation = .portrait
if size.width > size.height {
orientation = .landscape
}
guard orientation != lastOrientation else { return }
lastOrientation = orientation
guard let portraitConstraints = portraitConstraints, let landscapeConstraints = landscapeConstraints else {
print("No constraits")
return
}
var activeConstraints = portraitConstraints
var inactiveConstraints = landscapeConstraints
if orientation == .landscape {
activeConstraints = landscapeConstraints
inactiveConstraints = portraitConstraints
}
for constraint in inactiveConstraints {
constraint.isActive = false
}
for constraint in activeConstraints {
constraint.isActive = true
}
self.view.setNeedsLayout()
self.view.layoutIfNeeded()
}
}
This works, I wish it didn't and I'm hoping I've just done something seriously stupid ... other then the hacky workaround
If, you don't have much to do and want to muck around with the it, there is a source repo which I used to test the concept and come up with my "super, awesome" workaround, as I appreciate any kind of storyboard related question is a complete pain in the ... code to work with






