If you use a Storyboard, you can add auto layout constraints to a view using Interface Builder, and you can very easily add different constant
values for each size class you want. When you run the app and switch between size classes, the UI will automatically update and reposition to respect the correct constant
value for the new size class.
My question is how can you obtain that same behavior programmatically?
When you create an NSLayoutConstraint
you can't set different values for different size classes. I'm therefore thinking it would be a much more manual process. You'd have to create the constraints with the correct value for the current size class in viewDidLoad
for example, then you'd have to use willTransitionToTraitCollection
or maybe updateConstraints
and detect the new size class, then change the constant
s for all of the constraints that's appropriate for the new size class, then call layoutIfNeeded
on the views that need to be repositioned. That would be a lot of code, made worse the more size classes you optimize for. Is there not an easier and/or more efficient way to obtain that behavior programmatically?
Note that this question isn't limited to auto layout constraints but rather any object that can have its properties' values change based on size class. For example, setting a UILabel
's font for different size classes.
You need to create different sets of NSLayoutConstraint
.
Edit as per discussion below.
@Joey: You have to handle size classes decision in both viewDidLoad
(or similar) and viewWillTransitionToSize
.
Size class detection should be done inside the animateAlongsideTransition
block, not before.
Refactored code:
override func viewDidLoad() {
super.viewDidLoad()
let narrow = self.view.traitCollection.horizontalSizeClass
== UIUserInterfaceSizeClass.Compact
self.useNarrowConstraints(narrow)
}
override func viewWillTransitionToSize(size: CGSize,
withTransitionCoordinator
coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransitionToSize(size, withTransitionCoordinator: coordinator)
coordinator.animateAlongsideTransition({
(UIViewControllerTransitionCoordinatorContext) -> Void in
let narrow = self.view.traitCollection.horizontalSizeClass
== UIUserInterfaceSizeClass.Compact
self.useNarrowConstraints(narrow)
})
{ (UIViewControllerTransitionCoordinatorContext) -> Void in
}
}
Using Activation:
func useNarrowConstraints(narrow: Bool) {
if narrow {
NSLayoutConstraint.deactivateConstraints([self.fullWidthConstraint])
NSLayoutConstraint.activateConstraints([self.halfWidthConstraint])
} else {
NSLayoutConstraint.deactivateConstraints([self.halfWidthConstraint])
NSLayoutConstraint.activateConstraints([self.fullWidthConstraint])
}
}
Further details here.
Using Substitution:
func useNarrowConstraints(narrow: Bool) {
view.removeConstraint(constraint)
if narrow {
constraint = NSLayoutConstraint.constraintsWithVisualFormat("format", options: NSLayoutFormatOptions(0), metrics: nil, views: viewsDictionary)
} else {
constraint = NSLayoutConstraint.constraintsWithVisualFormat("otherFormat", options: NSLayoutFormatOptions(0), metrics: nil, views: viewsDictionary)
}
view.addConstraint(constraint)
}