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 tried to search the web for a solution to this, but am unable to find anything. Is there a way we can achieve this?
Here is what it looks like in portrait mode:
Here is what it looks like in landscape mode:
But this is what i want it to look like:
CodePudding user response:
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.
CodePudding user response:
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.
- You can add your constraints to a view and edit it at 'Size Inspector'.
- Double click the appropriate set constraint and add more required sizes classes using button just before the the label.
- Make sure to uncheck the 'Installed' checkbox and check the required sizes (wC,hC / wR, hR / wC, hR / wR, hC) for the selected constraints respectively (Constraints for portrait mode).
- Add new constraints for the UIs of landscape mode as well and do the same this as above no. 3 by checking only the size classes for iPhone landscape mode.
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/
CodePudding user response:
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