I am trying to create a pause screen on my game. I have added a 'PauseScreen' viewController in my storyboard with the Storyboard ID and Restoration ID set as "PauseScreenID" and to move to the pause screen I have have created the function in "GameScene" :
func pauseSceenTransition(){
let viewController = UIStoryboard(name: "Main", bundle:nil).instantiateViewControllerWithIdentifier("PauseScreenID") as UIViewController
let currentViewController = (UIApplication.sharedApplication().delegate as AppDelegate)
currentViewController.window?.rootViewController?.presentViewController(viewController, animated: false, completion: nil)
}
However when the it gets called, I get the error :
Warning: Attempt to present <AppName.PauseScreen: 0x7fae61fe5ff0> on <AppName.StartScreenViewController: 0x7fae61f79980> whose view is not in the window hierarchy!
The "StartScreenViewController" is the view controller for my start screen and is the initial view controller. It then goes to the "GameScene" which is where the "PauseScreen" needs to go. It works if I make the initial view controller the "GameViewController"/"GameScene" so I presume that I need to change the second line:
let currentViewController = (UIApplication.sharedApplication().delegate as AppDelegate)
so that it is presenting the "PauseScreen" on the "GameViewController", not on the "StartScreenViewController" but I'm not sure how to do that.
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.
Related
Working in multiple storyboards. Getting from point A->TabBarController(tab 2)->NavigationControllerA->StoryboardReference->NavigationControllerB->ViewControllerA->ViewControllerB. No need for data passing.
My tab bar controller is set up in its own storyboard, with each tab going through a navigation controller to a storyboard reference. Then the storyboard reference links to another storyboard (obv.), and through a navigation controller to ViewControllerA. I then want to go to performSegue to ViewControllerB. This is all done from a UIViewController Extension.
From what I've been reading, I should approach like this:
let tab = 2
let sbOne = UIStoryboard(name: "Main", bundle: nil)
let tabBarController = sbOne.instantiateViewController(withIdentifier: "TabBarController") as! UITabBarController
let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.window?.rootViewController = tabBarController
tabBarController.selectedIndex = tab
let sbTwo = UIStoryboard(name: "sbTwo", bundle: nil)
let viewControllerA = sbTwo.instantiateViewController(withIdentifier: "ViewControllerA")
performSegue(withIdentifier: "toViewControllerB", sender: self)
I receive NSInvalidArgument: Point A "has no segue with identifier 'toViewControllerB'"
Swift 3+
I’d be surprised if this is the best answer but this is the only solution that worked. A bit of my problem stemmed from lack of understanding. But the concept I arrived at was to separate the functions into two parts. Setting the tabbarcontroller tab, then segueing to the destination using a variable struct.
I ended up setting a struct at Point A.
struct Destination {
static var currentID = Int()
}
And on button press I would set the variable with this and then and call the function below:
Destination.currentID = currentID
goToViewControllerA()
Then using part of the code I had before I could set the tabbar Controller as the root view controller and go to ViewControllerA (the first view controller) on the selected tab. This is done in an uiviewcontroller extension but can also be done in a function in Point A.
goToViewControllerA(){
let tab = 2
let sbOne = UIStoryboard(name: "Main", bundle: nil)
let tabBarController = sbOne.instantiateViewController(withIdentifier: "TabBarController") as! UITabBarController
let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.window?.rootViewController = tabBarController
tabBarController.selectedIndex = tab
}
It’s then just a matter of checking if the struct static var is set, and segueing to the destination controller (ViewControllerB). This is done in ViewControllerA, or if you have to segue through multiple view controllers you could model your function on each viewcontroller using this in the viewdidload.
If Destination.currentID != Int() {
performSegue(withIdentifier:“toViewControllerBSegue”, sender:self)
}
I’d love to learn a better way of doing this if it exists.
I have a case in one of my Swift files which runs the following method:
case .editProfile:
vc = PoiDetailViewController()
self.navigationController?.popViewController(vc,animated: true)
It doesn't seem to work when I run the code.
The view controller is located on the main storyboard and I would like PoiDetailViewController to display when this case is called.
If I understand it correctly, you should be able to achieve what you're looking for by present in your code. Like so:
case .editProfile:
let storyBoard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyBoard.instantiateViewController(withIdentifier: "PoiDetailViewController") // or whatever identifier you have given it
self.present(vc, animated: true, completion: nil)
That should work, however, make sure you set a storyboard ID for your view controller in the identity inspector.
No segues are required.
To set up a storyboard ID click on the View Controller you would like to use (in this case, PoiDetailViewController) and click the identity inspector icon and set a storyboard ID where it asks. I have attached an image so you can see where it needs to go (in the field marked 'Storyboard ID')
Hope that helps.
As per my Understanding you need to pop to specific View Controller if it is in stack and if not then you need to push to that controller
var loginVCFound:Bool = false;
let navigationController : UINavigationController! = self.window!.rootViewController as! UINavigationController;
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil);
let viewControllers: [UIViewController] = navigationController.viewControllers
for aViewController in viewControllers {
if aViewController is LoginVC {// Pass the name of your controller here
loginVCFound = true;
navigationController.popToViewController(aViewController, animated: true)
break;
}
}
if !loginVCFound { // change the identifier and VC with yours
let objLoginVC = mainStoryboard.instantiateViewController(withIdentifier: "LoginVC") as! LoginVC
navigationController.pushViewController(objLoginVC, animated: true)
}
newbie here... my root view controller has the following code:
func showVC1() {
let storyBoard : UIStoryboard = UIStoryboard(name: "Main", bundle:nil)
let vc1 = storyBoard.instantiateViewController(withIdentifier: "first")
self.present(vc1, animated:true, completion:nil)
}
func showVC2() {
let storyBoard : UIStoryboard = UIStoryboard(name: "Main", bundle:nil)
let vc2 = storyBoard.instantiateViewController(withIdentifier: "second")
self.present(vc2, animated:true, completion:nil)
}
I can call these functions in either order, the first works fine, but the second will give me an error like "Attempt to present on whose view is not in the window hierarchy!"
I think I want to keep all of my code in the root vc that contains these functions. How can I make this work?
I'm also concerned that each time I call these functions I'll be creating new vc instances which will use more memory. Is there a way to keep a reference to these vc's outside of these functions? And will that solve the hierarchy issue?
Yes, second gives an error because you are trying to present two view controller at the same time from root view controller.
You can create lazy references to these view controllers
lazy var firstViewController : FirstViewController = {
let storyBoard : UIStoryboard = UIStoryboard(name: "Main", bundle:nil)
let vc1 = storyBoard.instantiateViewController(withIdentifier: "FirstViewController") as! FirstViewController
return vc1
}()
#IBAction func buttonPressed(_ sender: Any) {
self.present(firstViewController, animated:true, completion:nil)
}
Instead of presenting a new view controller try to push that one into the navigation stack like
self.navigationController.pushViewController(controller, animated: false)
Because if you present anything on your controller it will not continue the navigation. which will lead to crash
I am using the following code in AppDelegate to display a popup window within the app when a button is selected, i will eventually move this to a label hyperlink but just testing currently.
let storyboard = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("test") as! ViewController
let popOverVC = UIStoryboard(name:"Main", bundle: nil).instantiateViewControllerWithIdentifier("sbPopUpID") as! PopUpViewController
storyboard.addChildViewController(popOverVC)
popOverVC.view.frame = storyboard.view.frame
storyboard.view.addSubview(popOverVC.view)
popOverVC.didMoveToParentViewController(storyboard)
self.window?.rootViewController?.presentViewController(storyboard, animated: true, completion: nil)
The first time i select a button this works correctly, however on all subsequent button presses the following error is displayed.
2016-10-28 11:27:40.551 testfordeeplinks[20496:104536] Warning:
Attempt to present
on whose view is not
in the window hierarchy!
For anyone who stumbles across this in the future, this is how i resolved;
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
appDelegate.window?.rootViewController = storyboard
rootViewController doesn't necessarily refer to the view controller that is currently visible. For example, if you utilise a UINavigationController, rootViewController will hold a reference to that navigation controller which in itself is buried deep in the view hierarchy and hence when you try to present a view controller on it get an error. What I would recommend doing is grabbing the last view controller in navController.viewControllers and presenting your popup on that. See the following code:
let appDel = UIApplication.sharedApplication().delegate as! AppDelegate
if let navController = appDel.keyWindow?.rootViewController as? UINavigationController {
if let visibleVC = navController.viewControllers.last {
visibleVC.presentViewController(storyboard, animated: true, completion: nil)
}
}
Give that a try and let me know. :)
I have 3 items in my storyboard. 1. viewController (A) connected to 2. Navigation controller and 3. viewController (B) is NOT connected to anything
All 3 items have restoration identifiers set in the storyboard.
viewController (B) is the initial view controller.
I am trying in B to get the navigation controller that A is attached to, but always returns nil:
let storyBoard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyBoard.instantiateViewControllerWithIdentifier("viewControllerA") as! ViewControllerA
print(vc.navigationController?) // always prints nil
why!?
UPDATE#1
I can't declare a UINavigationController like a view controller. I've tried setting navigationcontroller with the id of 'myNavigationController' as storyboardID:
When storyboard, I get this error
let navigationController = storyBoard.instantiateViewControllerWithIdentifier("myNavigationController") as! UINavigationController
print(self.navigationController!) // fatal error: unexpectedly found nil while unwrapping an Optional value
I've also tried setting the id in restoration identifier, It bombed earlier at the instantiating line
#ColdLogic, see what hits I get when I search the entire project for that identifier:
Because you never instantiated the navigation controller, you instantiated view controller A. You would want something like this if you want both the nav controller and the view controller to be setup like they are in the storyboard
let storyBoard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let navigationController = storyBoard.instantiateViewControllerWithIdentifier("YourNavControllerIdentifier") as! UINavigationController
let vc = navigationController.topViewController as! ViewControllerA
Your code directly instantiates an object of type ViewControllerA. Which, unless you setup logic to do it, does not have a navigation controller by default.
This Worked for me!
let storyBoard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let navigationController = storyBoard.instantiateViewController(withIdentifier: "HomeNavigationContoller") as! UINavigationController
let viewController = storyBoard.instantiateViewController(withIdentifier: "HomeViewController") as! HomeViewController
navigationController.pushViewController(messageVC, animated: true)
self.present(navigationController, animated: true, completion: nil)
Use NavigationController identifier to access the NavigationController and ViewController A is already attached to it, so it will automatically get loaded to NavigationController