更改了 rootViewController 但旧 rootViewController 的视图仍在视图层次结构中

Changed rootViewController but the view of old rootViewController is still in the View Hierarchy

我有一个基本情况,当用户通过身份验证后,我删除当前屏幕(登录屏幕)并将其更改为应用程序内的另一个屏幕。

为此,我使用以下代码:

if let appDelegate = UIApplication.shared.delegate as? AppDelegate {
    print("Window's subviews before removed = \(appDelegate.window?.subviews)")

    appDelegate.window?.subviews.forEach { [=10=].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)")
}

这是输出:

这是用户可以在设备屏幕上看到的内容 - 看起来很不错:

但是,在 Debug View Hierarchy 工具中却不行:

如您所见,旧 rootViewController 的视图仍然存在,在 UIWindow 内,但不是它的子视图 - 如输出所示。

这个行为看起来很奇怪,有没有人遇到过这个问题?

使用此代码。无需手动删除视图。

appDelegate.window?.rootViewController = newRootViewController
appDelegate.window?.makeKeyAndVisible()

原因:

我在使用 Google 登录 SDK 和 Facebook 登录 SDK 登录时尝试替换 rootViewController 时遇到了这个问题。

这些 SDK 有一个身份验证屏幕,如下所示:

通过使用 Debug View Hierarchy,我意识到当显示身份验证屏幕 (2) 时,应用程序将 rootViewController 更改为 UITransitionView。并且当身份验证屏幕被关闭 (3) 时,它再次将 rootViewController 更改为呈现身份验证屏幕 (1) 之前的状态。

状态(1):在呈现认证画面之前,rootViewControllerLoginViewController

状态(2):呈现认证画面,rootViewController改为UITransitionView

状态(3):退出认证画面后,rootViewController又回到了LoginViewController

调试视图层次结构中的状态 (1) + (3):

调试视图层次结构中的状态 (2):

我把我的代码改成rootViewController在相应SDK的每个委托方法中,当认证完成时调用。

Google 登录SDK:signIn:didSignInForUser:withError:

Facebook 登录 SDK:logInWithReadPermissions:fromViewController:handler:

这些方法在用户通过身份验证后立即调用,而不管身份验证屏幕是否被关闭。

这意味着,有时,当用户的身份验证过程完成得太快时,甚至在身份验证屏幕关闭并且 rootViewController 更改为 LoginViewController 之前,就会出现问题。这意味着,当用户已通过身份验证但 rootViewController 仍然是 UITransitionView.

时,问题介于两种状态 (2) 和 (3) 之间

解决方法:

暂时的,在我找到更好的解决方案之前,我防止用户的身份验证过程发生得太快,这意味着我等待状态 (3) 在用户完成 delaying 0.25 秒后完成经过身份验证,然后更改 rootViewController.

0.25 是足够的时间让一切正常工作,但太快了用户会失去耐心。