Traversing from AppDelegate to other controllers - ios

The hierarchy i am using is something as follows ---
UIViewController->(present)->UINavigationController->(push)->UITableViewController.
How can I reach to 'UITableViewController' from my AppDelegate?

Add the following lines inside didFinishLaunchingWithOptions method in AppDelegate file
self.window = UIWindow(frame: UIScreen.mainScreen().bounds)
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
var initialViewController = mainStoryboard.instantiateViewControllerWithIdentifier("myTable") as! UITableViewController //myTable is the storyboard id of your tableviewcontroller
self.window?.rootViewController = initialViewController
self.window?.makeKeyAndVisible()

I think you could firstly define a nil property in AppDelegate:
var referenceTableVC:UITableViewController?
Then in your UITableViewContoller, in the end of the method ViewDidload(), give the property the reference:
var targetDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
targetDelegate.referenceTableVC = self

I got the answer...
let window = UIApplication.sharedApplication().delegate?.window!!
if let rootVC = window!.rootViewController {
if let presentCont = rootVC.presentedViewController {
//presentCont can be any controller in my case it's 'UINavigationController' from navigation controller I can get all the controllers..
//navigationController.viewControllers
}
}

Related

What happens to the previous window if I create a new window and makeKeyAndVisible

I am trying to logout and go back to the Login Viewcontroller.
So I create a window, set rootViewController, then makeKeyAndVisible. (Not in AppDelegate)
class AppManager: NSObject {
var window: UIWindow?
func goToLoginPage() {
window = UIWindow(frame: UIScreen.main.bounds)
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let loginVC = storyboard.instantiateViewController(withIdentifier:
"LoginVC") as! LoginVC
window?.rootViewController = loginVC
window?.makeKeyAndVisible()
}
}
It works, but I am thinking what will happen to the previous window. Will it be closed and released automatically? If not, do I need to do it?
Or is it better to directly use "UIApplication.shared.delegate?.window!!" like the following instead of creating a new window?
UIApplication.shared.delegate?.window!!.rootViewController = homeVC
You can get the existing window from the AppDelegate and change the rootViewControlller
if let appDelegate = UIApplication.shared.delegate as? AppDelegate, let window = appDelegate.window {
window.rootViewController = loginVC
}

Load A View Controller programmatically from a UIView

I am using the following from within a class to call a UIViewController. The UIViewController loads, but then becomes unresponsive after loading.
let storyboard : UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let homeC = storyboard.instantiateViewController(withIdentifier:
"ViewJoeProfileController") as? ViewJoeProfileController
if homeC != nil {
homeC!.view.frame = (self.window!.frame)
self.window!.addSubview(homeC!.view)
self.window!.bringSubview(toFront: homeC!.view)
}
}
Any suggestions to make the UIViewController being loaded responsive would really help!
If you specifically want to add to the window, you should do it the proper way and add the whole ViewController:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: NSDictionary?) -> Bool {
guard let window = UIWindow(frame: UIScreen.mainScreen().bounds) else { return true }
if let homeC = storyboard.instantiateViewController(withIdentifier: "ViewJoeProfileController") as? ViewJoeProfileController {
window.rootViewController = mainViewController
window.makeKeyAndVisible()
}
return true
}
But honestly that structure still doesn't seem correct. Why are you adding a view to the window, instead of using an initial view controller and then adding to itsviews, or segueing to different ViewControllers?.
Try using this I need to make root View Controller
let storyBoard = UIStoryboard.init(name: "Main", bundle: nil)
let vc = storyBoard.instantiateViewController(withIdentifier: "ViewJoeProfileController") as! ViewJoeProfileController
let nav = UINavigationController(rootViewController: vc)
nav.isNavigationBarHidden = true
self.window?.rootViewController=nav
self.window?.makeKeyAndVisible()

Saving VC on exit (IOS)

I have the code to save VC on exit.
It works but I use SWRevealViewController but the navigation controller in the application is not displayed
How can I correct it?
ViewController.swift
super.viewDidLoad()
NSUserDefaults.standardUserDefaults().setInteger(0, forKey: "View")
ViewController2.swift
super.viewDidLoad()
NSUserDefaults.standardUserDefaults().setInteger(1, forKey: "View")
AppDelegate.swift
let viewCount = UserDefaults.standard.integer(forKey: "View")
var VC = UIViewController()
let storyboard : UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
print(viewCount)
if viewCount == 0 {
VC = storyboard.instantiateViewController(withIdentifier: "First") as! ViewController
} else if viewCount == 1 {
VC = storyboard.instantiateViewController(withIdentifier: "Second") as! ViewController2
}
self.window?.makeKeyAndVisible()
self.window?.rootViewController = VC
return true
Error
My StoryBoard
So I think the issue is that you are setting the root view controller of the window to be a regular view controller. What you need to do is set this to be a navigation controller with your embedded view controllers. I'm not sure of your exact setup with SWRevealViewController but I guess you need something like this:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
let viewCount = UserDefaults.standard.integer(forKey: "View")
let storyboard : UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let VC = storyboard.instantiateViewController(withIdentifier: "First") as! ViewController
let frontNavigationController = UINavigationController(rootViewController: VC)
let VC2 = storyboard.instantiateViewController(withIdentifier: "TableView") as! TableView
let rearNavigationController = UINavigationController(rootViewController: VC2)
let revealVC = SWRevealViewController(rearViewController: rearNavigationController, frontViewController: frontNavigationController)
revealVC.delegate = self
self.window?.rootViewController = nil
self.window?.rootViewController = revealVC
self.window?.makeKeyAndVisible()
return true
}
If this isn't working, or what you are looking to achieve, please ask in comments.
EDIT:
So, if you want to display the TableView menu on the left, you have to set the storyboard id of it to be TableView (obvs set this in the storyboard). And then the code above should work.
I'm not entirely sure why you are using UserDefaults to save the VC. But you can now use the TableView class to navigate between VCs. If you want to save the last visited VC and display this on application finished launching, you would need this:
var string = ""
switch viewCount {
case 0:
string = "First"
case 1:
string = "Second"
case 2:
string = "Third"
case 3:
string = "Fourth"
case 4:
string = "Fifth"
default:
string = "First"
}
let VC = storyboard.instantiateViewController(withIdentifier: string)
let frontNavigationController = UINavigationController(rootViewController: VC)
Just make sure you have set the storyboard id of each VC to be "First", "Second", "Third" and so on. This would replace the previous declarations of let VC and let frontNavigationController in my above answer.
If in your storyboard your initial ViewController is UINavigationViewController then you can access this navigation controller in AppDelegate's didFinishLaunchingWithOptions as follows:
let navigationController = application.windows[0].rootViewController as! UINavigationController
Also, there is no need to track/save your view controllers in UserDefaults, you can always access the list of ViewControllers like this
AppDelegate
let childViewControllers = window?.rootViewController?.childViewControllers
print("childVC: \(String(describing: childViewControllers))")
In Any other ViewController
let childViewControllers = UIApplication.shared.keyWindow?.rootViewController?.childViewControllers
print("vcs : \(String(describing: childViewControllers))")
And of course, you can compare childViewControllers list
if let childViewControllers = UIApplication.shared.keyWindow?.rootViewController?.childViewControllers {
for viewController in childViewControllers {
if viewController is FirstViewController {
print("firstViewController found...")
}
if viewController is SecondViewController {
print("secondViewController found...")
}
}
}
In case if you're setting SWRevealViewController for side-menu using storyboard then the basic setup should be like
For more detail on SWRevealViewController have a look at their GitHub documentation, check sample project and mentioned tutorials here for deep understanding. Hope it helps.

Unable to instantiate rootViewController from within AppDelegate

My onboarding process (login/signup) lives outside of the UINavigationController chain, but when the login is authenticated I call a method in the appdelegate thats supposed to instantiate the rootViewController and then push to it. I've tried two things:
The snippet above worked in appDelegate from a method that gets called when a local notification is responded to, but not in this case.
self.window?.makeKeyAndVisible()
self.window?.rootViewController?.navigationController?.popToRootViewControllerAnimated(true)
And heres another method I tried:
let rootViewController = self.window?.rootViewController
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let dashboard = mainStoryboard.instantiateViewControllerWithIdentifier("DashboardViewController") as! DashboardViewController
rootViewController!.navigationController?.popToViewController(dashboard, animated: true)
Neither of these work. What am I doing wrong?
My login view controller also live outside of my tab bar controller so this is what I did:
In the appDelegate:
var window: UIWindow?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let loginVC = storyboard.instantiateViewControllerWithIdentifier("LoginVC") as! LoginViewController
self.window?.rootViewController = loginVC
self.window!.makeKeyAndVisible()
return true
}
then I have a method in my loginViewContoller.swift:
#IBAction func loginPushed(sender: AnyObject) {
//other stuff here
let mainStoryBoard = UIStoryboard(name: "Main", bundle: nil)
let tabBarController = UITabBarController()
let first = mainStoryBoard.instantiateViewControllerWithIdentifier("firstNavController") as! UINavigationController
let second = mainStoryBoard.instantiateViewControllerWithIdentifier("secondNavController") as! UINavigationController
let third = mainStoryBoard.instantiateViewControllerWithIdentifier("thirdNavController") as! UINavigationController
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let controllers = [first, second, third]
tabBarController.viewControllers = controllers
appDelegate.window!.rootViewController = tabBarController
appDelegate.window!.makeKeyAndVisible()
}
I think you shouldn't call the 'popToRootViewController', you can reassignment the window.rootViewController~

Opening view controller from app delegate using swift

I am trying to create a push notification which determines which view to open according to information obtained from the push.
I have managed to get the information from the push, but I am now struggling to get the view to open
Looking at other stack overflow questions I have the following currently:
App Delegate Did finish loading:
//Extract the notification data
if let notificationPayload = launchOptions?[UIApplicationLaunchOptionsRemoteNotificationKey] as? NSDictionary {
// Get which page to open
let viewload = notificationPayload["view"] as? NSString
let storyBoard : UIStoryboard = UIStoryboard(name: "Main", bundle:nil)
//Load correct view
if viewload == "circles" {
var viewController = self.window?.rootViewController?.storyboard?.instantiateViewControllerWithIdentifier("Circles") as! UIViewController
self.window?.rootViewController = viewController
}
}
Currently this is failing on the var ViewController = self... line.
You have to set ViewController StoryBoardId property as below image.
open viewController using coding as below in swift
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
let mainStoryboardIpad : UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let initialViewControlleripad : UIViewController = mainStoryboardIpad.instantiateViewControllerWithIdentifier("Circles") as UIViewController
self.window = UIWindow(frame: UIScreen.main.bounds)
self.window?.rootViewController = initialViewControlleripad
self.window?.makeKeyAndVisible()
return true
}
For iOS 13+ (based on an article by dev2qa)
Open SceneDelegate.swift and add following
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
// If this scene's self.window is nil then set a new UIWindow object to it.
self.window = self.window ?? UIWindow()
// Set this scene's window's background color.
self.window!.backgroundColor = UIColor.red
// Create a ViewController object and set it as the scene's window's root view controller.
self.window!.rootViewController = ViewController()
// Make this scene's window be visible.
self.window!.makeKeyAndVisible()
guard scene is UIWindowScene else { return }
}
There is an open-source navigation utility which attempts to make this easier. Example
Swift 3:
This is my preferred approach when presenting a new viewController from the current viewController through the AppDelegate. This way you don't have to completely tear down your view hierarchy when handling a push notification or universal link
if let controller = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "someController") as? SomeController {
if let window = self.window, let rootViewController = window.rootViewController {
var currentController = rootViewController
while let presentedController = currentController.presentedViewController {
currentController = presentedController
}
currentController.present(controller, animated: true, completion: nil)
}
}
Swift 3
To present the view together with the navigation controller:
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let viewController = storyboard.instantiateViewController(withIdentifier :"InboxViewController") as! InboxViewController
let navController = UINavigationController.init(rootViewController: viewController)
if let window = self.window, let rootViewController = window.rootViewController {
var currentController = rootViewController
while let presentedController = currentController.presentedViewController {
currentController = presentedController
}
currentController.present(navController, animated: true, completion: nil)
}
First Initialize the window
self.window = UIWindow(frame: UIScreen.mainScreen().bounds)
let storyBoard = UIStoryboard(name: "Main", bundle: nil)
For setting rootViewController inside AppDelegate Class
let viewController = storyBoard.instantiateViewControllerWithIdentifier("Circles") as UIViewController
self.window?.rootViewController = viewController
self.window?.makeKeyAndVisible()
There is a swift 4 version
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions:
[UIApplicationLaunchOptionsKey: Any]?) -> Bool {
let mainStoryboardIpad : UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let initialViewControlleripad : UIViewController = mainStoryboardIpad.instantiateViewController(withIdentifier: "Circles") as UIViewController
self.window = UIWindow(frame: UIScreen.main.bounds)
self.window?.rootViewController = initialViewControlleripad
self.window?.makeKeyAndVisible()
return true}
In Swift 3
let mainStoryboard : UIStoryboard = UIStoryboard(name: StorybordName, bundle: nil)
let initialViewControlleripad : UIViewController = mainStoryboard.instantiateViewController(withIdentifier: identifierName) as UIViewController
if let navigationController = self.window?.rootViewController as? UINavigationController
{
navigationController.pushViewController(initialViewControlleripad, animated: animation)
}
else
{
print("Navigation Controller not Found")
}
I'd say creating UIWindow each time you want to change rootViewController is bad idea. After couple changes of rootVC (using upper solution) you are gonna have many UIWindows in your app at one time.
In my opinion better solution is:
Get new rootVC: let rootVC = UIStoryboard(name: "StoryboardName", bundle: nil).instantiateViewControllerWithIdentifier("newRootVCIdentifier") as UIViewController
Set frame for new rootVC from UIScreen's bounds: rootVC.view.frame = UIScreen.mainScreen().bounds
Set new root controller for current window (here with animation): UIView.transitionWithView(self.window!, duration: 0.5, options: .TransitionCrossDissolve, animations: {
self.window!.rootViewController = rootVC
}, completion: nil)
Done!
You don't need method window?.makeKeyAndVisible(), cause this solution works on current app window.
Swift 3 SWRevealViewController
self.window = UIWindow(frame: UIScreen.main.bounds)
let storyBoard = UIStoryboard(name: "Main", bundle: nil)
let viewController = storyBoard.instantiateViewController(withIdentifier: "SWRevealViewController") as! SWRevealViewController
self.window?.rootViewController = viewController
self.window?.makeKeyAndVisible()
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let destinationViewController = storyboard.instantiateViewController(withIdentifier: "LandVC") as! LandingPageVC
destinationViewController.webpageURL = NotificationAdvertisement._htmlpackagePath
destinationViewController.adID = NotificationAdvertisement._adID
destinationViewController.toneID = NotificationAdvertisement.toneID
let navigationController = self.window?.rootViewController as! UIViewController
navigationController.showDetailViewController(destinationViewController, sender: Any?.self)
SWIFT 4
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let destinationViewController = storyboard.instantiateViewController(withIdentifier: "LandVC") as! LandingPageVC
destinationViewController.webpageURL = NotificationAdvertisement._htmlpackagePath
destinationViewController.adID = NotificationAdvertisement._adID
destinationViewController.toneID = NotificationAdvertisement.toneID
let navigationController = self.window?.rootViewController as! UIViewController
navigationController.showDetailViewController(destinationViewController, sender: Any?.self)
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = (scene as? UIWindowScene) else { return }
self.window = UIWindow(windowScene: windowScene)
let mainStoryboard : UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let vc : UIViewController = mainStoryboard.instantiateViewController(withIdentifier: "LoginViewController")
let rootNC = UINavigationController(rootViewController: vc)
self.window?.rootViewController = rootNC
self.window?.makeKeyAndVisible()
}

Resources