Adding a View to the Window Hierarchy

Adding a view to the window hierarchy

The error is telling you exactly what's going on.

Warning: Attempt to present <AppName.PauseScreen: 0x7fae61fe5ff0> on <AppName.StartScreenViewController: 0x7fae61f79980> whose view is not in the window hierarchy!

(UIApplication.sharedApplication().delegate as AppDelegate).window?.rootViewController is pointing to an instance of StartScreenViewController. This is bad: rootViewController should point to an instance of GameScene.

The root cause must be how GameScene is presented. From your description:

The "StartScreenViewController" is the view controller… It then goes to the "GameScene"…

This must be where your problem is. How do you go to GameScene from StartScreenViewController?

My guess is that you are adding a new window to the application. You need to set the rootViewController instead.

let gameScene = UIStoryboard(name: "Main", bundle:nil).instantiateViewControllerWithIdentifier("GameSceneID") as UIViewController
let appDelegate = (UIApplication.sharedApplication().delegate as AppDelegate)
appDelegate.window?.rootViewController = gameScene

When you go back to the start screen, you again set the rootViewController.

let initialViewController = UIStoryboard(name: "Main", bundle:nil).instantiateInitialViewController() as UIViewController
let appDelegate = (UIApplication.sharedApplication().delegate as AppDelegate)
appDelegate.window?.rootViewController = initialViewController

You can use transitionFromViewController(,toViewController:, duration:, options:, animations:, completion:) to animate setting the root view controller.

How do I add a view to the top of an app in iOS 13/14

You have to create a second UIWindow, assign the rootViewController and make it visible.

The code bellow is for SwiftUI, but you can do the same with UIKit, just create a window and set window.isHidden = false.

let secondWindow = UIWindow(windowScene: windowScene)
secondWindow.frame = CGRect(x: 0, y: 40, width: UIScreen.main.bounds.size.width, height: 100)
let someView = Text("I am on top of everything")
secondWindow.rootViewController = UIHostingController(rootView: someView)
secondWindow.isHidden = false

Depending on how many windows you have. You might need to change the windowLevel of your second window.
You can check if the window is displayed using Debug View Hierarchy from Xcode.

This is a UIKit example without SceneDelegate. If you have scene delegate you have to pass the window scene to the UIWindow init.
Don't forget to retain the second window.


@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
private enum Constants {
static let sessionConfiguration = URLSessionConfiguration.default
}

var window: UIWindow?
var secondWindow: UIWindow?

// MARK: - UIApplicationDelegate

func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool {

let window = UIWindow(frame: UIScreen.main.bounds)
self.window = window
let viewController = UIViewController()
viewController.view.backgroundColor = .red

window.rootViewController = viewController
window.makeKeyAndVisible()

let secondWindow = UIWindow()
secondWindow.frame = CGRect(
x: 0,
y: 40,
width: UIScreen.main.bounds.size.width,
height: 100
)

let secondController = UIViewController()
secondController.view.backgroundColor = .blue
secondWindow.rootViewController = secondController
secondWindow.isHidden = false
self.secondWindow = secondWindow

return true
}
}

Sample Image

presenting whose view is not in the window hierarchy warning

Your LoginVC is perfectly fine.

However, you need to change your HomeVC as @Sh_Khan suggested and move the testing code from viewDidLoad to viewDidAppear:

import UIKit
import Firebase

class HomeVC: UIViewController {

override func viewDidLoad() {
super.viewDidLoad()
}

// HomeVC.view was added to a view hierarchy
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)

// to check whether the user has already logged in or not
Auth.auth().addStateDidChangeListener { (auth, user) in
if user == nil {
let login = self.storyboard?.instantiateViewController(withIdentifier: "login") as! LoginVC
self.present(login, animated: true, completion: nil)
}
}
}
}

Explanation

Your viewDidLoad method gets called before the viewController gets presented, so it at that moment it cannot really present another view controller (since it itself is not presented), viewDidLoad documentation:

Called after the controller's view is loaded into memory.

This method is called after the view controller has loaded its view hierarchy into memory. This method is called regardless of whether the view hierarchy was loaded from a nib file or created programmatically in the loadView() method. You usually override this method to perform additional initialization on views that were loaded from nib files.

In that moment the viewController is not in the window hierarchy yet.

viewDidAppear however gets called when the view is presented and becomes a part of the window hierarchy, viewDidAppear documentation:

Notifies the view controller that its view was added to a view hierarchy.

You can override this method to perform additional tasks associated with presenting the view. If you override this method, you must call super at some point in your implementation.

Don't forget to call super.viewDidAppear during overriding it.

Root View Controller's view not in the Window Hierarchy

you can get the root view controller of the window, which should be the navigation controller, and then get its top view controller.

try like this:if you are sure there is a viewController.presentedViewController then send includeModal as YES.

- (UIViewController *)topmostViewControllerFrom:(UIViewController *)viewController
includeModal:(BOOL)includeModal
{
if ([viewController respondsToSelector:@selector(selectedViewController)])
return [self topmostViewControllerFrom:[(id)viewController selectedViewController]
includeModal:includeModal];

if (includeModal && viewController.presentedViewController)
return [self topmostViewControllerFrom:viewController.presentedViewController
includeModal:includeModal];

if ([viewController respondsToSelector:@selector(topViewController)])
return [self topmostViewControllerFrom:[(id)viewController topViewController]
includeModal:includeModal];

return viewController;
}

Swift 4 Attempt to present ViewController whose view is not in the window hierarchy

This issue happens due to your view hierarchy.

You need to find out what is your Current/Topmost view controller in
view hierarchy and present your alert over it.

To find out topmost view controller use following code:

func getTopMostViewController() -> UIViewController? {
var topMostViewController = UIApplication.shared.keyWindow?.rootViewController

while let presentedViewController = topMostViewController?.presentedViewController {
topMostViewController = presentedViewController
}

return topMostViewController
}

And present your alert over topmost view controller and use main thread to present an alert because closures may have working on another thread.

DispatchQueue.main.async { 
getTopMostViewController()?.present(alertController, animated: true, completion: nil)
}

Please refer to this stack answer:
Swift 3 Attempt to present whose view is not in the window hierarchy

SwiftUI - Timing of views getting added to the window hierarchy when using a List

It is a bit early, activate full screen on view appeared, like below (tested with Xcode 12 / iOS 14)

struct ComplicatedView: View {
@State private var showingCover = false

var body: some View {
DetailsOfComplicatedView()
.fullScreenCover(isPresented: $showingCover, content: {
TutorialView()
})
.onAppear { self.showingCover = true }
}
}

Swift 3 Attempt to present whose view is not in the window hierarchy

I have resolved the issue the main problem is exactly what it says the view is not in the view hierarchy. In order to resolve this issue we need to set the root view controller to the current view controller using the appDelegate object. So that the view now comes in the view hierarchy and be able to present further views. Here is the code

let initialViewController = UIStoryboard(name: "Main", bundle:nil).instantiateInitialViewController() as UIViewController
let appDelegate = (UIApplication.sharedApplication().delegate as AppDelegate)
appDelegate.window?.rootViewController = initialViewController

Please read this for further information. https://stackoverflow.com/a/27608804/5123516

Is MyViewController in the window hierarchy?

The view controller's view will have its window property set if its in the hierarchy

if MyViewController.view.window != nil {
// In the window hierarchy
}

iOS whose view is not in the window hierarchy

That happen because of two viewcontroller present and dismiss at a same time or you are trying to present ViewController immediately at the viewcontroller open ViewDidload method so

First:

  • Present ViewController from viewDidAppear Method or instead of ViewDidload.

Second:

I suggest to make use of completion method for present and dismiss viewcontrolelr like following example:

[self presentViewController:lOTPViewController animated:YES
completion:^{

}];

UPDATE:

Create a separate method of presenting a OTPViewController like following:

-(void)PresentOTPViewController
{

UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
OTPViewController *lOTPViewController = [storyboard instantiateViewControllerWithIdentifier:@"OTPViewController"];
lOTPViewController.comingFromReg = true;

[self presentViewController:lOTPViewController animated:YES
completion:^{}];

}

Now call this method with 1 second Delaya using performSelector

[self performSelector:@selector(PresentOTPViewController) withObject:self afterDelay:1.0 ];

You need to put above performselect code in

[self dismissViewControllerAnimated:YES completion:^{
[self performSelector:@selector(PresentOTPViewController) withObject:self afterDelay:1.0 ];
}]; // this is the dismiss method of PassCodeViewController

t



Related Topics



Leave a reply



Submit