I have a basic situation, when the user has been authenticated, I remove and change the current screen (the login screen) to another screen inside the app.
To do this, I use this code:
if let appDelegate = UIApplication.shared.delegate as? AppDelegate {
print("Window's subviews before removed = \(appDelegate.window?.subviews)")
appDelegate.window?.subviews.forEach { $0.removeFromSuperview() }
print("Window's subviews after removed = \(appDelegate.window?.subviews)")
appDelegate.window?.rootViewController?.view?.removeFromSuperview()
appDelegate.window?.rootViewController?.removeFromParentViewController()
appDelegate.window?.rootViewController = newRootViewController
print("Window's subviews after changed = \(appDelegate.window?.subviews)")
}
This is the output:

This is what the user can see on the device screen - looks very OK:

However, it isn't ok in the Debug View Hierarchy tool:

As you can see, the view of old rootViewController is still there, inside UIWindow but not a subview of it - as the output has indicated.
This behavior seems strange, has anyone experienced this problem yet?
I have this problem when I try to replace rootViewController while logged in using the Google Sign-In SDK and Facebook Login SDK.
These SDKs have an authentication screen like this one:

By using the Debug View Hierarchy, I realized when the authentication screen was presented (2), the app changed the rootViewController to a UITransitionView. And when the authentication screen is dismissed (3), it changed the rootViewController once more to the state before presenting the authentication screen (1).
State (1): Before presenting the authentication screen, rootViewController is LoginViewController.
State (2): Presenting the authentication screen, rootViewController changed to UITransitionView.
State (3): After dismissed the authentication screen, rootViewController has returned to LoginViewController.
States (1) + (3) in Debug View Hierarchy:

State (2) in Debug View Hierarchy:

I put my code to change the rootViewController in the each delegate method of corresponding SDKs that is called when authentication is completed.
Google Sign-In SDK: signIn:didSignInForUser:withError:
Facebook Login SDK: logInWithReadPermissions:fromViewController:handler:
These methods are called immediately after the user has authenticated without regard to the authentication screen is dismissed or not.
It means, sometimes, the problem occurs when the user's authentication process is completed too quickly, even before the authentication screen is dismiss and the rootViewController changes to LoginViewController. It means, the problem is in between two states (2) and (3), when the user has authenticated but rootViewController is still UITransitionView.
Temporary, before I can find a better solution, I prevent the user's authentication process from happening too quickly, it means that I wait for state (3) to finish by delaying 0.25 seconds after the user had authenticated and then changing rootViewController.
0.25 is enough time for everything to work and too fast for the user to lose patience.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With