Instantiate View Controller not working - swift

I'm trying to load another UIViewController so that I can get the data of the IBOutlets inside of it. I'm using Swift and Sprite-Kit. The UIViewController I am talking about is called GameViewController. It is the first thing that loads up in my game. Then it goes to my GameScene. This is where I am trying to instantiate my GameViewController. I want to do this because I am using a library called iCarousel and I want to animate it moving up from my GameScene. However, iCarousel can only be used on a UIViewController. So I want to call a function in GameViewController that will be called in GameScene. In this function, there is an NSLayoutConstraint that is used to move the carousel. Whenever I call this function, it says that its nil. Here is some of my code:
Inside my GameScene:
let storyboard: UIStoryboard = UIStoryboard.init(name: "Main", bundle: nil)
let firstViewController: GameViewController = storyboard.instantiateViewControllerWithIdentifier("Load-up") as! GameViewController
firstViewController.start()
Inside my GameViewController:
func start() {
// Constraint
top.constant = 100
UIView.animateWithDuration(2){
self.view.layoutIfNeeded()
}
}
If you need more information, feel free to ask me. Thanks in advance!

That is because your view controller hasn't loaded its view hierarchy yet. viewController loads its view hierarchy only when something sends it a view message. The system will do this by its own when to put the view hierarchy on the screen. And it happens after calls like prepareForSegue:sender: and viewWillAppear: , loadView(), self.view.
So here your outlets are still nil since it is not loaded yet.
Just try to force your VC to call self.view and then access the properties/outlets.
Edit: Added sample code.
let storyboard = UIStoryboard.init(name: "Main", bundle: nil)
let firstViewController = storyboard.instantiateViewControllerWithIdentifier("Load-up") as! GameViewController
debugPrint("Printing for the sake of initializing view of VC: \(firstViewController.view)")
firstViewController.start()

Related

Swift -injecting vars in ViewControllers

I am proudly new to iOS developing and I am trying to build my first app. I am doing a course on an online platform which does the following in the
AppDelegate -> application didFinishLaunchingWithOptions:
let navigationController = window?.rootViewController as! UINavigationController
let notebooksListViewController = navigationController.topViewController as! NotebooksListViewController
notebooksListViewController.dataController = dataController
This app has a Navigation controller which begins with an UIViewController.
I have 2 questions here, first is why this works, I mean, I am in AppDelegate, so the NotebooksListViewController (first view of the app) is not instantiated yet (I think), so why I am able to inject a variable in it?
On the other hand, the second question, is how can I do this in a different scene? I have a TabBarViewController as first scene, and the first tab is a UITableViewController and I want to inject the same way my dataController var, how can I accomplish this? I could not get to do it, neither understand it.
Thanks in advance.
It works, because of some Xcode magic:
In your Target Setting, General tab, the Main Interface entry specifies the name of the Storyboard to be loaded automatically when your app starts up:
In the storyboard, the Initial View Controller then will be instantiated. It seems like this is an UINavigationController.
Since this is done automatically, it just works - until you want to do something special :-)
If you want to start up with a different scene - maybe from a different view controller - you could just change either the Main Interface to another storyboard, the Initial View Controller (inside the storyboard) or both.
Or, you could just start up by yourself, by leaving the Main Interface empty and create your own view controller inside the app delegate (didFinishLaunchingWithOptions), something like
self.window = UIWindow(frame: UIScreen.mainScreen().bounds)
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
if let tabVC = mainStoryboard.instantiateViewControllerWithIdentifier("TabCtrl") as? UITabBarController {
self.window?.rootViewController = tabVC
// Access the subcontrollers, or create them
// Initialize their values
// tabVC.viewControllers[0].data = ...
} else {
// Ooops
}
self.window?.makeKeyAndVisible()
Answer to your first question
as the method name is self explanatory didFinishLaunchingWithOptions means your application is didfinish with launching with options and its about to enter in foreground so here application need to set rootViewController so in this method controller you want to set as view controller is initiated thats why you can inject variable in it
answer to second question
let navigationController = window?.rootViewController as! UITabbarController
let VC = navigationController.childViewController
//Now Using VC you can access all you controller of tabbar controller
let notebooksListViewController = navigationController.topViewController as!
NotebooksListViewController
notebooksListViewController.dataController = dataController
now as shown above you can use VC to access you view controllers
but be careful here because VC return viewcontroller array so you need make checks for perticular VC you want to access

How to pop to root controller in app where root viewcontroller is of type UIViewController?

I have implemented SWRevealViewController class for revealing a rear (left and/or right) view controller behind a front controller. SWRevealViewController of type UIViewController is the entry point in the app.
What do I want?
In class AppDelegate when applicationDidEnterBackground is called, I want to pop all the view controllers on the stack except the root view controller and updates the display.
What I tried?
if let appDelegate = UIApplication.shared.delegate as? AppDelegate {
appDelegate.window?.rootViewController?.dismiss(animated: true, completion: nil)
}
I know that if SWRevealViewController were embedded in a UINavigationController, I could simply do (appDelegate.window?.rootViewController as? UINavigationController)?.popToRootViewController(animated: true) , but it is not embedded in nav controller and if I embed it, then I will not get the same behaviour, so how can I sort this out?
I believe what you are trying to do is an "unwindSegue".
They are are documented in a technical note:
https://developer.apple.com/library/content/technotes/tn2298/_index.html
With that you should be able to identify your root view controller as the target of the unwind segue and, of needed, follow the unwind through various steps as it occurs.
You can also look at the documentation for the various methods of UIViewController that contain the term "unwind"
https://developer.apple.com/documentation/uikit/uiviewcontroller
Try This:
let navVC = ((UIApplication.shared.delegate as? AppDelegate)?.window)?.rootViewController as? UINavigationController
for VC: UIViewController in (navVC?.viewControllers)! {
if (VC is MYViewController) {
navVC?.popToViewController(VC, animated: true)
break
}
}

Opening window + view from an other view controller

I've got a ViewControllerOne. ViewControllerOne is connected via Ctrl-Drag (in storyboard) to a menu-button mBtn (which means I don't know how it is implemented programmatically).
Clicking on this mBtn, a ViewOne appears (present modal). This ViewOne is bound to ViewControllerOne. ViewOne has a button btnOne.
Clicking on btnOne I want ViewOne to be dismissed and ViewTwo to be shown. ViewTwo belongs to ViewControllerTwo and to WindowControllerTwo.
The WindowControllerTwo-ViewControllerTwo-binding is the standard case as created on a new project.
I have the following code in the IBAction for button btnOne in ViewControllerOne:
#IBAction func onbtnOnePressed(sender: AnyObject){
let m_WindowControllerTwo = NSStoryboard(name: NSStoryboard.Name(rawValue: "Main"), bundle: nil).instantiateController(withIdentifier: NSStoryboard.SceneIdentifier("WindowControllerTwo")) as! NSWindowController // I have no custom class for the window controller as I don't really know what I can use it for ...
let m_ViewTwo = WindowControllerTwo.contentViewController as! ViewControllerTwo // my custom class for ViewTwo
m_ViewTwo.attributeIWantToPassToThisView = self.x // an attribute I want to pass from view a to view b
m_WindowControllerTwo.contentViewController = m_ViewTwo // passing the attribute from a to b
m_WindowControllerTwo.showWindow(self) // this does not work
self.dismiss(nil) // see NOTE
}
This code actually does not work. On debugging it step by step, I'm seeing the window/view flickering but not appearing...
NOTE: I could connect the button btnOne with a ctrl-drag to ViewControllerTwo. This works. But then the current ViewOne does not get dismissed!
Question: What am I doing wrong here? In iOS swift this also works. I don't quite get the WindowController stuff, so I'll need your advice on this.
Instead of this: m_WindowControllerTwo.showWindow(self)
use:
let application = NSApplication.shared()
application.runModal(for: wordCountWindow) //this will present WindowControllerTwo modally.
then to close your present controller add this line: PresentWindowControllerName.close()

Why is drawRect not called in the NSView when I programmatically load my window from a storyboard?

I have a window in my Main.storyboard file that I am trying to load programmatically, and not at application launch. So I have deleted the "Storyboard Entry Point" entirely, and in AppDelegate.swift, I have:
let storyBoard = NSStoryboard(name: "Main", bundle: nil)
let pauseWindowController = storyBoard.instantiateControllerWithIdentifier("pauseWindowController")
pauseWindowController.showWindow(self)
This seems to work, but when I run the app, I get no window. I added tracing code in my app to follow the progression of events, and I find that my custom NSView subclass is initialized, but drawRect is never called. If I remove the above code above and re-add the Storyboard Entry Point (to point to the window controller), everything works fine.
Here is the result of my print calls that I added for tracing, which show the calling class and the method called:
(AppDelegate) applicationDidFinishLaunching
(Pause OverlayWindow : NSWindow) Window init
(PauseOverlayView : NSView) View init
(PauseViewController: NSViewController) viewDidLoad
(PauseViewController: NSViewController) viewDidAppear
(PauseViewController: NSViewController) viewWillDisappear
(PauseViewController: NSViewController) viewDidDisappear
There should be a line (PauseOverlayView : NSView) drawRect line after viewDidAppear, and the window should display, but the method is never called. Why?
I have "Visible At Launch" checked in the Storyboard editor for my window. To my understanding, drawRect is called if the view is dirty and visible. I imagine it's not visible for some reason, but can't find out why.
I found my mistake: I didn't add the pauseWindowController property (var pauseWindowController : NSWindowController!) to the top of my AppDelegate.swift file. And the code should then become
let storyBoard = NSStoryboard(name: "Main", bundle: nil)
pauseWindowController = storyBoard.instantiateControllerWithIdentifier("pauseWindowController") as! NSWindowController
pauseWindowController.showWindow(self)

Present View from SKScene in Xcode 6 with Swift

I have a basic game running exclusively as SKScene. I would like to transition to a view called testview.xib but I cannot find any Swift examples of how to do so.
Here's my attempt based on other answers
let vc = UIViewController(nibName: "testview", bundle: nil) as UIViewController
self.view.window.rootViewController.presentViewController(vc, animated: true, completion: nil)
but the application fails with an error "Thread1: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode 0x0)"
The code for moving between SKScenes is
var transition:SKTransition = SKTransition.flipHorizontalWithDuration(1)
var scene:SKScene = otherScene(size: self.size)
self.view.presentScene(scene, transition: transition)
This turned out to be an issue with calling presentViewController() from within the init() method of the class. Basically, you can't call presentViewController() before the view/controller is setup; instead, you'll typically want to call it when some action occurs (button press, touch event, etc.).
The issue that was starting to be discussed in the comments and then moved in to chat was solved by loading the view controller from a storyboard instead of a xib. NOTE: An xib would work, but it's trickier to setup than a storyboard is.