Some background:
The app has a TabBarController. TabBarController has VC1.1 and VC2.1.
The app supports only portrait for iPhone and for 1 screen that we call VC1.2 (that I will mention later) it will show it in landscape mode.
But for iPad we support both portrait and landscape.
VC2.1 is a Controller which has NavigationController. This Screen 2.1 can push the new screen 2.2. Important note is that the screen 2.1 and 2.2 on iPad are presented together in a split view. But on iPhone we present it only separately in 2.1 and 2.2.
So how the crash happens.
The crash reproduces only with iPhone+ or X Max.
Open VC2.1 and press the button to push VC2.2.
Go back to VC1.1 and press button to present the screen VC1.2 that must be in landscape.
The app crashes with this info:
Warning: Attempt to present on whose view is not in the window hierarchy!
Unbalanced calls to begin/end appearance transitions for .
[Assert] Trying to dismiss the presentation controller while transitioning already. (<_UIFullscreenPresentationController>)
[Assert] transitionViewForCurrentTransition is not set, presentation controller was dismissed during the presentation? (<_UIFullscreenPresentationController)
So what happens is that on VC1.2 we are show the screen that we force to be in landscape mode.
And in VC2.2 (and VC2.1) since there is a SplitView (even though for iPhone it is not used) it somehow tries to rotate it and "show" (or pretend showing) that split view. And it crashes because it can't do it.
So we understand why this happens but can't understand if there is a way to fix it or is it an iOS issue maybe.
If it helps, in AppDelegate we have this method:
private func defaultInterfaceOrientations(for window: UIWindow?) -> UIInterfaceOrientationMask {
let isPhone: Bool
if let window = window {
isPhone = window.traitCollection.userInterfaceIdiom == .phone
} else {
isPhone = UIDevice.current.userInterfaceIdiom == .phone
}
return isPhone ? .portrait : .all
}
EDIT1:
There is one very interesting thing that happens in this flow and which may give an additional hint.
Steps:
Open VC2.1. Press button to push VC2.2. Press back button (pop).
Go to VC1.1. Open the landscape screen V1.2. It won't crash. Meaning it crashes only when VC2.2 is opened, but when VC2.1 is opened - it doesn't crash.
The interesting point: when you open the VC2 (tab bar item) you would expect it to be on VC2.1 since we went back (pop). But for some reason it will be on screen VC2.2. So somehow in the "back" it made a push of VC2.2.
Related
iOS 16 introduced a new API for forcing orientation changes, which implies using the requestGeometryUpdate(_:errorHandler:).
It seems that it's not working as expected when using a full screen modal.
For example, let's say we have an app that locks the orientation in portrait mode except for a modally presented controller, a parent controller A and a modal controller B :
A is displayed
We put the device in landscape mode - A stays in portrait mode
B is opened modally fullscreen from A
B is displayed in landscape
B has a button to go back to portrait mode using the requestGeometryUpdate(_:errorHandler:)
B is dismissed through a button
A ends up in landscape
In the app delegate, we used supportedInterfaceOrientationsFor to force the app in portait mode except for the controller B.
We tried to call setNeedsUpdateOfSupportedInterfaceOrientations every time we call requestGeometryUpdate(_:errorHandler:).
We also tried to call it on the viewWillAppear from the controller A.
We finally tried to call requestGeometryUpdate(_:errorHandler:) from the controller A's viewWillAppear, without any luck as well.
Is this a bug in iOS 16 or are we missing something ?
It seems to be working as expected when displaying the modal over the current context, but it's not what we want in this case.
I can provide an example project if needed.
I have been trying to change the device orientation on a button click event for 2 days but I am not able to achieve it properly.
So, I have an iPhone app with a minimum version of 12.4, and the device that I am using to test is 14.4. I have used the code mentioned in this project here (which works properly when presenting a new view controller using push or present) but the same doesn't work when on the same view controller. The user needs to rotate his/her device to initiate the orientation change.
This is the code that I have in the view controller that I am trying to rotate on a button click event.
let appDelegate = AppDelegate.sharedInstance
let oldOrientaion = appDelegate.deviceOrientation
appDelegate.deviceOrientation = oldOrientaion == .landscapeRight ? .portrait : .landscapeRight
let value = appDelegate.deviceOrientation.rawValue
UIDevice.current.setValue(value, forKey: "orientation")
Can someone please help me in figuring out what exactly is missing from this?
Also, on a side note, the view controller is presented on a different view controller so even if I try to present a new view controller like in the project for some time and change the orientation there it doesn't work.
Edit:
This is fixed now. I was trying to use the UIInterfaceOrientationMask to set the orientation instead of UIInterfaceOrientation. So changing the code to use UIInterfaceOrientation at the end works properly now.
Thanks for the help.
You are doing everything correctly, just need to call the attemptRotationToDeviceOrientation method to the oration change to take effect. Based on the documentation:
Some view controllers may want to use app-specific conditions to
determine what interface orientations are supported. If your view
controller does this, when those conditions change, your app should
call this class method. The system immediately attempts to rotate to
the new orientation.
In your code it will look like:
UIViewController.attemptRotationToDeviceOrientation()
In iOS there is a new behaviour for modal view controller when being presented. Now it's not fullscreen by default and when I try to slide down, the app just dismiss the View Controller automatically.
How can I prevent this behaviour and get back to the old good fullscreen modal vc? I face this problem while using appodeal framework.
Code to show videoad
if Appodeal.isReadyForShow(with: .nonSkippableVideo){
Appodeal.showAd(.nonSkippableVideo, rootViewController: self)
}
This is my situation: I push a ViewController that is in landscape mode, then go to the home screen. When I go back in the app, the previous views in the back stack flash for just a moment before going to my ViewController. This doesn't happen for portrait ViewControllers.
Has anyone seen this before or know of a workaround? Thanks!
For the record, I am simply pushing the ViewController via:
self.navigationController!.pushViewController(viewController, animated: true);
and then forcing the view into landscape mode, not sure if that's relevant.
What solved this problem for me was calling
window?.makeKeyAndVisible();
in the AppDelegate file
I'm making an iPad application using a UISplitViewController. I want the masterView visible in a UIPopoverController when the app starts (and only when it starts) in portrait mode. If I use the presentPopoverFromBarButtonItem:permittedArrowDirections:animated: method in the splitViewController:willHideViewController:withBarButtonItem:forPopoverController:
delegate function, I get the following error, when I start the app in portait mode:
Popovers cannot be presented from a view which does not have a window.
Can anybody help me?
The particular error says that you must add the view to a window before showing the popover, not the other way round. Try sending presentPopover… from the application delegate's -application:didFinishLaunchingWithOptions: after adding the view to your app's window.