Detecting orientation on loading - ios

When my simulator is in portrait and when my viewcontroller loads initially, it prints out Landscape instead of Portrait but when I change the orientaiton, it correctly displays the orientation so forth. I did the following
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if (UIDevice.current.orientation == UIDeviceOrientation.portrait || UIDevice.current.orientation == UIDeviceOrientation.portraitUpsideDown)
{
print("Portrait")
}
else
{
print("Landscape")
}
}
I have no idea why it is displaying wrong orientation when it loads initially but once I rotate everything seems to work.
P.S. It seems like when simulator initially loads, the orientaiton is unknown, so it is choosing the else condition, how to avoid this from happening and identify the correct orientaiton?

You can check current orientation by UIApplication.shared.statusBarOrientation.isPortrait or UIApplication.shared.statusBarOrientation.isLandscape.

if UIDevice.currentDevice().orientation.isLandscape.boolValue {
print("landscape")
} else {
print("portrait")
}

Related

Detect correct iPad orinatation

I have to detect the orientation of the device and so something configuration based on it.
I write a simple function to handle it
public func isLandscape() -> Bool {
return UIScreen.main.bounds.width > UIScreen.main.bounds.height
}
It works perfectly on iPhone, but on iPad it's not. Sometimes when iPad is on landscape, it shows that it's UIScreen.main.bounds.height is larger than UIScreen.main.bounds.width, which is super weird.
So for iPad, I change to it like that
public func isLandscape() -> Bool {
if DeviceType.isPhone {
return UIScreen.main.bounds.width > UIScreen.main.bounds.height
} else {
return UIDevice.current.orientation == .landscapeLeft || UIDevice.current.orientation == .landscapeRight || UIDevice.current.orientation == .unknown
}
}
But still not working probably on iPad. The case of unknown is called sometimes on portrait and sometimes on landscape.
Is there any other way, that could works flawlessly? I really need to have a prefect way to always show the the iPad and iPhone that they are in portrait and landscape? Those unknown, faceDown and faceUp are really confusing and make problems in our workflow.
Your help will be appreciated
if(UIApplication.shared.statusBarOrientation.isLandscape)
{
print("[UI] Landscape")
}
else
{
print("[UI] Portriat")
}
You will receive a warning saying that .statusBarOrientataion is deprecated, but I tested with Xcode 14 on an iPad Air 4th generation (iPadOS 15.7), and iPhone 12 (iOS 16), and it worked.

Detect the landscape orientation left or right

My app supports portrait and landscape -> Both left and right. I am able to detect if its landscape. But not able to detect left or right. Here is my code
if UIDevice.current.orientation.isLandscape {
// Do some task
}
When user rotates the device i need to detect whether the user rotated to landscape left or landscape right !
Same way inside my above condition I need to check whether its left or right side. How can I detect that?
Thanks
I think you are looking for something like this
if UIDevice.current.orientation == UIDeviceOrientation.landscapeLeft {
} else if UIDevice.current.orientation == UIDeviceOrientation.landscapeRight {
} else {
//not landscape left or right
}
EDIT --------
based on your comments you are looking for interface orientation instead of device orientation.
override func didRotate(from fromInterfaceOrientation: UIInterfaceOrientation) {
var text=""
switch UIDevice.current.orientation{
case .portrait:
text="Portrait"
case .portraitUpsideDown:
text="PortraitUpsideDown"
case .landscapeLeft:
text="LandscapeLeft"
case .landscapeRight:
text="LandscapeRight"
default:
text="Another"
}
NSLog("You have moved: \(text)")
}
the code above detects interface orientation...
note how the switch statement still uses UIDeviceOrientation
Below is another method that you may want to use
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
if UIDevice.current.orientation.isLandscape {
print("landscape")
} else {
print("portrait")
}
}
Note again that UIDevice Orientation is still used...
Below is a very different but effective approach.
struct DeviceInfo {
struct Orientation {
// indicate current device is in the LandScape orientation
static var isLandscape: Bool {
get {
return UIDevice.current.orientation.isValidInterfaceOrientation
? UIDevice.current.orientation.isLandscape
: UIApplication.shared.statusBarOrientation.isLandscape
}
}
// indicate current device is in the Portrait orientation
static var isPortrait: Bool {
get {
return UIDevice.current.orientation.isValidInterfaceOrientation
? UIDevice.current.orientation.isPortrait
: UIApplication.shared.statusBarOrientation.isPortrait
}
}
}}
I am doing it like this for now:
public extension UIScreen {
public class var isLandscapeLeft: Bool { UIApplication.shared.statusBarOrientation == .landscapeLeft }
public class var isLandscapeRight: Bool { UIApplication.shared.statusBarOrientation == .landscapeRight }
}
It's as extension to UIScreen just for convenience so its easy to find..
Also to detect when app actually rotates from left to right when I have just landscape enabled, I had to use AppDelegate's function:
func application(_ application: UIApplication, didChangeStatusBarOrientation oldStatusBarOrientation: UIInterfaceOrientation) { ... }
For whatever reason nothing else get's triggered.
Looks like also, you have to use UIApplication.shared.statusBarOrientation when checking from didChangeStatusBarOrientation instead of UIApplication.shared.delegate?.window??.rootViewController?.interfaceOrientation I used for other situations.

How to check if device in landscape or portrait mode in flat position

There are some questions about device orientation and some users answered like this:
if UIDevice.current.orientation.isLandscape {
print("landscape")
} else {
print("portrait")
}
Which is totally wrong because they don't know that it can have the third result: isFlat
So it can return three different results:
if UIDevice.current.orientation.isLandscape {
print("landscape")
} else if UIDevice.current.orientation.isFlat {
print("flat")
} else { // UIDevice.current.orientation.isPortrait
print("portrait")
}
So if a device is in flat position (isFlat) how to check if that device shows content in landscape or portrait mode?
Update
Only one of them can be true
If isFlat returns true then both isLandscape and isPortrait return false
Update 2
I ended up using:
private func isLandscape() -> Bool {
return self.view.frame.width > self.view.frame.height
}
Or we can use UIApplication.shared.statusBarOrientation.isLandscape

UIView.isHidden not working when changing orientation using viewWillTransition

I'm trying to change orientation with just one view, the rest are anchored to Portrait. I've set a method in my AppDelegate as below
func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
if globalVariables.gIsDosageView == "Y" {
if UIDevice.current.orientation == .landscapeLeft || UIDevice.current.orientation == .landscapeRight {
return UIInterfaceOrientationMask.all;
} else {
return UIInterfaceOrientationMask.portrait;
}
} else {
return UIInterfaceOrientationMask.portrait;
}
}
The global variable gIsDosageView is set to "Y" when the Dosage view is selected. I've created two separate views, portraitView (375x812) and landscapeView (812x375). I've used the viewWillTransition method to catch each change in orientation as below
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
if size.width < 400 {
self.userImg.isHidden = false
self.deviceImg.isHidden = false
self.portraitView.isHidden = false
self.landscapeView.isHidden = true
}
else {
self.userImg.isHidden = true
self.deviceImg.isHidden = true
self.portraitView.isHidden = true
self.landscapeView.isHidden = false
}
}
When I go from the main screen to Dosage it displays the correct screen and will toggle between both orientation views without issue. However, if I go to another screen and come back to the Dosage screen it only shows the screen that was first loaded in both orientation screens. I've stepped through the code and it hides the right screens but this is not reflected in the resulting view. If I select the screen in portrait first, it will toggle successfully between portrait and landscape but if I go to the next screen and return to Dosage, it will only show the Portrait screen regardless of orientation and appears to ignore the code in viewWillTransition().
Why is this and what have I missed?
viewWillTransition is only called when the device is rotated. When you switch between open views without rotating, it won't get called, so won't set the view up how you want it. Try putting the test in viewWillAppear instead.

UISplitViewController - prevent splitting in landscape on iPhone 6 plus

I am using a UISplitViewController in my app. This works just fine on iPad where primary and secondary are always visible, and it works just fine on most iPhones where it acts like a UINavigationController.
On iPhone 6+ and 6S+ the split view acts like an iPhone in portrait and like an iPad in landscape. This splitting in landscape is causing me problems and I'd like to avoid it.
Is there any way to prevent the UISplitViewController from showing primary and secondary controllers in iPhone 6+ landscape? I just want it to show the secondary controller, the same as it would do for other iPhones.
Thanks.
I was able to do this by subclassing the UISplitViewController and then overriding the trait collection to return a compact horizontal size class when the device is not an iPad. I know checking the interface idiom is a faux-pas these days, but I wasn't sure how else to do it.
I simply added this method to my UISplitViewController subclass:
-(UITraitCollection *)traitCollection {
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
return [super traitCollection];
} else {
return [UITraitCollection traitCollectionWithHorizontalSizeClass:UIUserInterfaceSizeClassCompact];
}
}
Any suggestions for a better way to do this are certainly welcome.
Here is the same answer in Swift but also with a fix where the vertical size class would be wrong on phone in landscape:
override var traitCollection: UITraitCollection {
if UI_USER_INTERFACE_IDIOM() == .pad {
return super.traitCollection
} else {
let horizontal = UITraitCollection(horizontalSizeClass: .compact)
let vertical = UITraitCollection(verticalSizeClass: super.traitCollection.verticalSizeClass)
return UITraitCollection.init(traitsFrom: [horizontal, vertical])
}
}
I had some issues with UINavigationControllers not displaying correctly with the code above. This is the method that worked for me (Swift 5):
1) Create a UIViewController containing a UIContainerView
2) Embed your UISplitViewController in that container
3) Add the following code:
class SplitViewContainerViewController: UIViewController {
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
if UI_USER_INTERFACE_IDIOM() != .pad {
performOverrideTraitCollection()
}
}
private func performOverrideTraitCollection() {
for childVC in self.children {
setOverrideTraitCollection(UITraitCollection(horizontalSizeClass: .compact), forChild: childVC)
}
}
}
4) Set the view controller containing the container view to be SplitViewContainerViewController
Update For iOS 13
The code above no longer works on iOS 13. Use the following instead on the SplitViewContainerViewController class:
override func overrideTraitCollection(forChild childViewController: UIViewController) -> UITraitCollection? {
if UIDevice.current.userInterfaceIdiom != .pad {
return UITraitCollection(horizontalSizeClass: .compact)
} else {
return super.traitCollection
}
}

Resources