willTransition not triggered when flipping device in landscape mode

Hello,


The following function is triggered whenever the device is rotated, except in the case when I "flip" the device along it's X axis when in landscape mode.


override func willTransition(to newCollection: UITraitCollection, with coordinator: UIViewControllerTransitionCoordinator) {

print("orientation after activity = " + String(UIDevice.current.orientation.rawValue))

}


If I hold a device in landscape mode, and then rotate it away from me, so that the screen is facing away, the view rotates, but the willTransition func is not triggered. So if I am starting out in landscape left with the device facing me, and then do my "flip", it's essentially now landscape right with the screen facing away from me.


I am building a "flip mode" in my app where I want two specific views to rotate, but I don't want the entire window to rotate. I can't seem to figure out how to detect this transition so that I can properly handle it in my code.

Answered by Claude31 in 357732022

I did some more tests, with a CollectionView



    override func willTransition(to newCollection: UITraitCollection, with coordinator: UIViewControllerTransitionCoordinator) {

        super.willTransition(to: newCollection, with: coordinator)
        print("orientation after activity = " + String(UIDevice.current.orientation.rawValue), newCollection)
    }


Then, I rotate Left (from Portrait): newCollection for Landscape Left

orientation after activity = 3 <UITraitCollection: 0x6000010d8b00; _UITraitNameUserInterfaceIdiom = Phone, _UITraitNameDisplayScale = 2.000000, _UITraitNameDisplayGamut = sRGB, _UITraitNameHorizontalSizeClass = Compact, _UITraitNameVerticalSizeClass = Compact, _UITraitNameUserInterfaceStyle = 1, _UITraitNameUserInterfaceLayoutDirection = 0, _UITraitNameForceTouchCapability = 1, _UITraitNamePreferredContentSizeCategory = UICTContentSizeCategoryL>


Then, rotaote right and right again

newCollection for Portrait

orientation after activity = 1 <UITraitCollection: 0x6000010d1280; _UITraitNameUserInterfaceIdiom = Phone, _UITraitNameDisplayScale = 2.000000, _UITraitNameDisplayGamut = sRGB, _UITraitNameHorizontalSizeClass = Compact, _UITraitNameVerticalSizeClass = Regular, _UITraitNameUserInterfaceStyle = 1, _UITraitNameUserInterfaceLayoutDirection = 0, _UITraitNameForceTouchCapability = 1, _UITraitNamePreferredContentSizeCategory = UICTContentSizeCategoryL>


newCollection for Landscape Right

orientation after activity = 4 <UITraitCollection: 0x6000010d4b00; _UITraitNameUserInterfaceIdiom = Phone, _UITraitNameDisplayScale = 2.000000, _UITraitNameDisplayGamut = sRGB, _UITraitNameHorizontalSizeClass = Compact, _UITraitNameVerticalSizeClass = Compact, _UITraitNameUserInterfaceStyle = 1, _UITraitNameUserInterfaceLayoutDirection = 0, _UITraitNameForceTouchCapability = 1, _UITraitNamePreferredContentSizeCategory = UICTContentSizeCategoryL>


We can see that UITraitCollection only differ by _UITraitNameVerticalSizeClass = Compact or Regular

But for landscape Left or Right, they are exactly the same.


Hence, when you flip, UITraitCollection does not change.


Looking at doc:

willTransition(to:with:)

Notifies the container that its trait collection changed.


So, when you flip, UITraitCollection does not change at all. Hence, willTransition is not called.


What I understand is that

- if x, y are axes of the plane of the phone screen and z the axis from front to back

- willTransition detects rotation along the z-axis but not along x or y (your flip is along y-axis)


As a conclusion: the behavior is normal even though a bit disappointing not to be able to detect the flip.


Maybe you could detect the flip directly (with motion sensors).

But there is probably a simpler way: detect that the whole view has flipped.


I added in ViewController:


    override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
        super.viewWillTransition(to: size, with: coordinator)
        print("orientation of view = \(size)")
    }

Then, rotating Left (to Landscape) and then change orientation directly to Landscape Right, I get

orientation after activity = 3 <UITraitCollection: 0x600002002e00; _UITraitNameUserInterfaceIdiom = Phone, _UITraitNameDisplayScale = 2.000000, _UITraitNameDisplayGamut = sRGB, _UITraitNameHorizontalSizeClass = Compact, _UITraitNameVerticalSizeClass = Compact, _UITraitNameUserInterfaceStyle = 1, _UITraitNameUserInterfaceLayoutDirection = 0, _UITraitNameForceTouchCapability = 1, _UITraitNamePreferredContentSizeCategory = UICTContentSizeCategoryL>

orientation of view = (568.0, 320.0)

orientation of view = (568.0, 320.0)


Which show that viewWillTransition was triggered. Even though the size did not change !


So, behavior is a bit inconsistent across APIs.

You could file a bug or enhancement report.



I will have to manually do this swapping. It'll be a little tricky, since I am using autolayout,

As for adapting the content, you certainly have to do it in code.

I would do it by changing the constraints in code: you probably have just a bunch of them (I guess, an enclosing view for each team, then just have to change some trailing and leading constraints, may be horizontal spacing as well).

Have you checked:

- which orientations are authorized in info.plist


You may also have to add this in your viewController



override open var supportedInterfaceOrientations : UIInterfaceOrientationMask     {
        return .all
    }



Note that iPhoneX series (with notch) does not support portrait upside down.

Thanks for replying. I have them all checked, and evern tried to override the supported interface orientations as you suggested. The window rotates, but I don't get an event in willTransition.

You wrote:

If I hold a device in landscape mode, and then rotate it away from me, so that the screen is facing away, the view rotates,…


what is the end configuration: portrait upside or downside ?

Don't you ever get a print, whatever rotation ? (from landscape to portrait, from portrait to landscape) ?


Did you notice:

If you override this method in your own objects, always call super at some point in your implementation so that UIKit can forward the trait changes to the associated presentation controller and to any child view controllers. View controllers forward the trait change message to their child view controllers. Presentation controllers forward the trait change to their presented view controller.

I do get other print statements, just nothing from when I do the "flip" from landscape left facing me to screen facing away from me (which is landscape right if you were looking at it from opposite me).


I'll make sure to handle super as well.

>is triggered whenever the device is rotated


Sorry if I missed it - which device (not simulator)?


In what orientation is the app designed to launch? In what orientation examples are you in when the app is launched?

I am facing the same problem.

A navigation app needs to update the headingOrientation property on CLLocationManager to get heading updates relative to the user interface orientation. So I wanted to use [UIViewController viewWillTransitionToSize:withTransitionCoordinator:] to detect user interface orientation changes, but this method is not called when the device if flipped from landscape left to right via upside-down (which is not supported and therefore not reported).

Alternatives:

  • UIDeviceOrientationDidChangeNotification's may not be in sync with UIKit when the user has the orientation lock activated
  • [UIViewController didRotateFromInterfaceOrientation:] is deprecated

EDIT: The solution is to use [UIViewController viewWillTransitionToSize:withTransitionCoordinator.] which is called even though the size is not changed.

Use:

func application(_ application: UIApplication, willChangeStatusBarOrientation newStatusBarOrientation: UIInterfaceOrientation, duration: TimeInterval) { ... }

func application(_ application: UIApplication, didChangeStatusBarOrientation oldStatusBarOrientation: UIInterfaceOrientation) { ... }

They get triggered...

willTransition not triggered when flipping device in landscape mode
 
 
Q