I am building a calculator app and have it working in portrait mode. However, I want the user to have more options/functions when they switch to landscape mode. In other words, the entire layout would have to change as there will be more buttons in landscape mode compared to portrait mode.
I have managed to do this using the installed
property under the attribute inspector. Here are the problems I am running into:
What am I doing wrong? How can I resolve this?
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.
You can use iOS orientation change method in view controller class life cycle.
First you have to make all the appropriate UIs using stackViews and views UI objects.
Then you need to manage your constraints (width, height, ratio of the calculator UI buttons/views using iOS size classes.
Also add this method in your view controller and manage your views visibility
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
if UIDevice.current.orientation.isLandscape {
print("Landscape")
// Show your additional UIs elements views for Landscape
} else {
print("Portrait")
// Hide your additional UIs elements / views for Portrait
}
}
If you want any assistance for making UI sizes, please follow the option below. So you have to use iOS size classes.
Please review the checking options in Xcode here
Helpful links:
https://medium.com/swlh/ios-adaptive-layout-i-size-class-c561bd730e1 https://developer.apple.com/design/human-interface-guidelines/ios/visual-design/adaptivity-and-layout/
I made a sample work for you and added to My GitHub repositories.
Please have a look on main.storyboard UI setup and get your work done.
Repo: https://github.com/ryra-circuit/DynamicUICalculator
Thanks
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.