Here's what I've been doing since iOS9. Complete code, does not assume you are using IB (I actually think you should not) and should help you with how auto layout works. BEWARE, this does assume some knowledge of auto layout and Swift. This also deals with a very simple example of a UIButton.
First, declare two arrays of constraints, typically in the view controller:
var landscape = [NSLayoutConstraint]()
var portrait = [NSLayoutConstraint]()
var myButton = UIButton()
Next, set up your constant constraints and set the active:
myButton.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(myButton)
myButton.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 10).isActive = true
myButton.heightAnchor.constraint(equalToConstant: 40).isActive = true
myButton.widthAnchor.constraint(equalToConstant: 200).isActive = true
So far, so good. You've created a button, added it to the VC view, declared it's size as 40x200. You've also declared it to be anchored to the parent's safeAreaLayoutGuide top anchor.
Now, it's time for the "magic". You wish to move this UIButton from the bottom to the right depending on orientation. (Please note, while certain overrides will work on iPhones, iPads, when full screen, will always have a "Regular" size class - in full screen - no matter the orientation!)
The first thing you need is to put the remaining constraints into the arrays you declared.
portrait.append(myButton.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 10))
landscape.append(myButton.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -10))
Now you have s single constraint to activate. It can be more, remember we are talking arrays. This is simple, just deactivate and activate as needed:
func changeConstraints() {
if isInPortrait {
NSLayoutConstraint.deactivate(landscape)
NSLayoutConstraint.activate(portrait)
} else {
NSLayoutConstraint.deactivate(portrait)
NSLayoutConstraint.activate(landscape)
}
}
You can even animate things is you wish! Finally, let's tap into the VC life cycle. I'm very sure there are other (and better) ways, but keep in mind what I mentioned earlier - in full screen, an iPad will always have a Regular size class. So I prefer to dig just a bit deeper. I tend to use three variables - you probably need less.
var initialOrientation = true
var isInPortrait = false
var orientationDidChange = false
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
if initialOrientation {
initialOrientation = false
if view.frame.width > view.frame.height {
isInPortrait = false
} else {
isInPortrait = true
}
changeConstraints()
} else {
if view.orientationHasChanged(&isInPortrait) {
changeConstraints()
}
}
}
I also have an extension to UIView:
extension UIView {
func orientationHasChanged(_ isInPortrait:inout Bool) -> Bool {
if self.frame.width > self.frame.height {
if isInPortrait {
isInPortrait = false
return true
}
} else {
if !isInPortrait {
isInPortrait = true
return true
}
}
return false
}
}
I think this should describe most of my code. Basically, (a) do not use UIDevice nor (b) use viewWillTransition(toSize:) and check a size class when working with a iPad. (They will have a size class change - albeit differently on devices depending on size, but only when in split screen mode. At least for now!)
Pretty much, use (a) view willLayoutSubviews - you can use view DidLayoutSubviews but I prefer doing things at the earliest point possible - and then (b) check the actual screen size.