PresentViewController is showing nil of my RootViewController - ios

So let me explain.
I have two ViewControllers (Login-Page and Main-Page).
Login page contains two buttons (FB and G+ login buttons).
Everything works fine with login credentials and is presenting the next view which is Main-Page.
In Main-Page I have button (sign-out).Button should work as singing out and bring me back to Login-Page, unfortunately is not working.
Below is my code
let vc = self.storyboard?.instantiateViewController(withIdentifier: "LoginPage") as! LoginPage
vc.modalTransitionStyle = UIModalTransitionStyle.crossDissolve
self.present(vc, animated: true, completion: nil)
Is showing
fatal error: unexpectedly found nil while unwrapping an Optional value
and the output is nil for all declared IBOutlet in Main-Page
Please I need help!!!??

First we verify if the view controller has a Storyboard ID on the Identity inspector:
Then:
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let loginViewController = storyboard.instantiateViewController(withIdentifier: "LoginPage")
let window = UIApplication.shared.windows[0] as UIWindow
UIView.transition(with: window,
duration: 0.5,
options: UIViewAnimationOptions.transitionCrossDissolve,
animations: {
window.rootViewController = loginViewController
}, completion: nil)
With that, you are replacing the presented view controller for your login view controller.

Try using notifications to change the root when you did logout for the app.
First in your appDelegate addObserver:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Notification used in logout action
let notf = NSNotificationCenter.defaultCenter()
notf.addObserverForName("logout", object: nil, queue: NSOperationQueue.mainQueue()) { (notification) in
let controller = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("LoginPage") as! LoginPage
self.window?.rootViewController = controller
}
return true
}
then in the logoutAction simply postNotification
func logout() {
NSNotificationCenter.defaultCenter().postNotificationName("logout", object: nil)
}

Related

Presenting different UIViewController on the app Launch

I need to present different UIViewController on the launch of my app. I have a "login" page for the user to interact with. Once the user logs in or creates an account it takes to a different UIViewController with a map to interact with. I've looked online and so far I know that we should do it within AppDelegate.swift file. I have not completed the statement whether or not the user has logged in since I am still running into some errors
AppDelegate
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
Thread.sleep(forTimeInterval: 2.0)
window = UIWindow(frame: UIScreen.main.bounds)
window?.makeKeyAndVisible()
window?.rootViewController = MainNavigationContoller()
return true
}
I also have another swift file with MainNavigationContoller that should call the mainviewController
override func viewDidLoad() {
super.viewDidLoad()
let isloggedIn = false
if isloggedIn == false {
self.present(mainViewController(), animated: true, completion: nil)
} else {
self.present(mapViewController(), animated: true, completion: nil)
}
}
The app launches with the launchScreen but then sends errors to mainViewController such as Thread 1: Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value
You are not instantiating the View Controllers properly.
Here is a function that I use to make a root view controller, put this into your AppDelegate.
func makeRootVC(storyBoardName : String, vcName : String) {
let vc = UIStoryboard(name: storyBoardName, bundle: Bundle.main).instantiateViewController(withIdentifier: vcName)
let nav = UINavigationController(rootViewController: vc)
nav.navigationBar.isHidden = true
self.window?.rootViewController = nav
let options: UIView.AnimationOptions = .transitionCrossDissolve
let duration: TimeInterval = 0.6
UIView.transition(with: self.window!, duration: duration, options: options, animations: {}, completion: nil)
}
Now in your Appdelegate, replace your code with this:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
Thread.sleep(forTimeInterval: 2.0)
self.makeRootVC(storyboardName: "Main", vcName : "YourVCStoryboardId")
return true
}
and in your MainNavigationController,
override func viewDidLoad() {
super.viewDidLoad()
let isloggedIn = false
let appDelegateObj = UIApplication.shared.delegate as! AppDelegate
if isloggedIn == false {
appDelegateObj.makeRootVC(storyboardName: "Main", vcName: "mainViewController")
} else {
appDelegateObj.makeRootVC(storyboardName: "Main", vcName: "mapViewController")
}
}
NOTE: Open storyboard and give every controller a StoryboardID. What I prefer is naming them the same as ViewController's name as it is easy to remember. and in vcName, we need to pass the storyboarID of the controller we want to present.
UPDATE:
Above code is for making a root view controller, if you want to push controllers, you can use this code instead:
extension UIViewController {
func pushVC(storyboardName : String, vcname : String) {
let vc = UIStoryboard.init(name: storyboardName, bundle: Bundle.main).instantiateViewController(withIdentifier: vcname)
vc.hidesBottomBarWhenPushed = true
self.navigationController?.pushViewController(vc, animated: true)
}
}
In your MainNavigationController if instead of making root view controllers in viewDidLoad, you want to just push the controller, then use the above code like this:
override func viewDidLoad() {
super.viewDidLoad()
let isloggedIn = false
if isloggedIn == false {
self.pushVC(storyboardName: "Main", vcName: "mainViewController")
} else {
self.pushVC(storyboardName: "Main", vcName: "mapViewController")
}
}

Shortcut Items, Quick Actions 3D Touch swift

I am for some reason having some troubles figuring out the Shortcut Items in my app. I have followed all the tutorials and I believe that I have done everything right; however, it is acting really weird. The problem is that when you completely close out that app and do the 3D Touch Shortcut item on the home screen, it takes you to the correct part of the app as it should; however, when the app is open in the background (like you just click the home button) it doesn't take you to the correct part of the app, it just opens it. This is really odd and I am not sure what to do because I have followed all the instructions. Thank you so much!
Code:
func application(_ application: UIApplication, performActionFor shortcutItem: UIApplicationShortcutItem, completionHandler: #escaping (Bool) -> Void) {
if shortcutItem.type == "com.myapp.newMessage" {
let sb = UIStoryboard(name: "Main", bundle: nil)
let vc = sb.instantiateViewController(withIdentifier: "RecentVC") as! UITabBarController
vc.selectedIndex = 2 //this takes me to the contacts controller
let root = UIApplication.shared.keyWindow?.rootViewController
root?.present(vc, animated: false, completion: {
completionHandler(true)
})
} else if shortcutItem.type == "com.myapp.groups" {
let sb = UIStoryboard(name: "Main", bundle: nil)
let vc = sb.instantiateViewController(withIdentifier: "RecentVC") as! UITabBarController
vc.selectedIndex = 1 //this takes me to the groups controller
let root = UIApplication.shared.keyWindow?.rootViewController
root?.present(vc, animated: false, completion: {
completionHandler(true)
})
} else {
//more short cut items I will add later
}
}
NOTE: these are static Shortcut Items, and I configured them in the info.plist file, This function that was presented is the only place in the app besides the info.plist file that have anything about the Shortcut Items.
EDIT: This seems like an issue with the RootViewController, because I have tried multiple different ways of doing this, and it still gives me the same warning message
2017-11-22 22:36:46.473195-0800 QuickChat2.0[3055:1068642] Warning: Attempt to present on whose view is not in the window hierarchy!
I just tried to accomplish my goals this way through this post but it gives me the exact same results as before.
You should set a variable in your performActionForShortcutItem that tells you the shortcut type and move the code you have in there into applicationDidBecomeActive. Your could should look like this
var shortcut: String?
func applicationDidBecomeActive(application: UIApplication) {
if let shortcutItem = shortcut {
shortcut = nil
if shortcutItem == "com.myapp.newMessage" {
let sb = UIStoryboard(name: "Main", bundle: nil)
let vc = sb.instantiateViewController(withIdentifier: "RecentVC") as! UITabBarController
vc.selectedIndex = 2 //this takes me to the contacts controller
let root = UIApplication.shared.keyWindow?.rootViewController
root?.present(vc, animated: false, completion: nil)
} else if shortcutItem == "com.myapp.groups" {
let sb = UIStoryboard(name: "Main", bundle: nil)
let vc = sb.instantiateViewController(withIdentifier: "RecentVC") as! UITabBarController
vc.selectedIndex = 1 //this takes me to the groups controller
let root = UIApplication.shared.keyWindow?.rootViewController
root?.present(vc, animated: false, completion: nil)
} else {
//more short cut items I will add later
}
}
}
func application(_ application: UIApplication, performActionFor shortcutItem: UIApplicationShortcutItem, completionHandler: #escaping (Bool) -> Void) {
shortcut = shortcutItem.type
completionHandler(true)
}
I think a cleaner method is to set your flags in the performActionFor method, then check for them once your VC loads. Using this notification will handle the shortcut every time the app becomes active, including launch.
override func viewDidLoad() {
super.viewDidLoad()
// Sends notification if active app is reentered to handle quick actions
NotificationCenter.default.addObserver(self, selector: #selector(handleQuickActions), name: UIApplication.didBecomeActiveNotification, object: nil)

Trying to Embed VC into NavigationVC from performActionFor shortcutItem via 3d touch on App icon

I have an app that is using 3d touch to jump to a certain VC in my app. The issue is that when the app is launched normally, all of my VC's are embedded into a Navigation View Controller. But since I am skipping the launch sequence and jumping right into a VC, the VC that I jump to isn't embedded into the Nav VC.
Here is what I am trying in the App Delegate
func application(_ application: UIApplication, performActionFor shortcutItem: UIApplicationShortcutItem, completionHandler: #escaping (Bool) -> Void) {
guard TouchActions(rawValue: shortcutItem.type) != nil else {
print("3d not working")
completionHandler(false)
return
}
print("3d touch workig")
let mainStoryboard:UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let addPersonVC : AddPersonViewController = mainStoryboard.instantiateViewController(withIdentifier: "AddPerson") as! AddPersonViewController
// pass the stack to the addPersonVC
addPersonVC.coreDataStack = coreDataStack
let navController:UINavigationController = mainStoryboard.instantiateViewController(withIdentifier: "navController") as! UINavigationController
navController.pushViewController(addPersonVC, animated: true)
// self.window? = UIWindow(frame: UIScreen.main.bounds)
self.window?.rootViewController = navController
self.window?.makeKeyAndVisible()
completionHandler(true)
}
This code works, but you when I try to leave this VC, the app just sits there unresponsive. I some how need to embedded addPersonVC into the main Nav VC that is set up in storyboard. (The embedded navigation controller was set up in storyboard, incase that matters.)
Option1: Add a Storyboard Identifier to your UINavigationController , and instantiate the UINavigationController instead.
Option2: Delete the UINavigationController from storyboard. Just initialize a new UINavigationController by code and set the rootView programatically.
I am not a Swift developer, but since you don't see the examples I wrote I did some quick pasting to help you understand the basics, try this code:
let mainStoryboard:UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let addPersonVC : AddPersonViewController = mainStoryboard.instantiateViewController(withIdentifier: "AddPerson") as! AddPersonViewController
// pass the stack to the addPersonVC
addPersonVC.coreDataStack = coreDataStack
let navController:UINavigationController = mainStoryboard.instantiateViewController(withIdentifier: "navController") as! UINavigationController
navController.viewControllers = [addPersonVC]
self.window?.rootViewController?.present(navController, animated: true, completion: nil)
self.window?.makeKeyAndVisible()

Deciding the initial view controller before app finish loading

Hi I have a login view controller and my app storyboard views. The initial view controller points to the app dashboard. I am checking in my appDelegate application function if the user is logged in. After that I (re)set the correct UIViewController as the initial. Somehow the app always goes to the initial view controller and then says oops I need to go to login because the user is not logged in... How can I avoid this?
This is my current code.
dispatch_async(dispatch_get_main_queue(), {
let storyboard: UIStoryboard = UIStoryboard.init(name: "Main", bundle: nil)
if (settings.isLoggedIn())
{
let vc: AnyObject! = storyboard.instantiateInitialViewController()
self.window?.rootViewController = vc as? UIViewController
} else {
let vc: AnyObject! = storyboard.instantiateViewControllerWithIdentifier("login")
self.window?.rootViewController = vc as? UIViewController
}
})
how to check user logged in or not in below method in the appdelegate
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
//check user logged or not, if true you can set the initial view controller given below
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc : UIViewController! = storyboard.instantiateViewControllerWithIdentifier("urcontroller")
let window = UIApplication.sharedApplication().windows[0] ;
window.rootViewController = vc;
return true
}

didReceiveRemoteNotification presentViewController from anywhere when running

I'm trying to present a UIViewController when a remote notification is received.
My code works to point, but when the app is running and the user is on any other than the first screen/navigation stack, the UIViewController isn't presented.
Can anyone help please? Note I want to keep the navigation bar when the UIViewController is presented
I get this warning when I try to present the 'UIViewController' elsewhere
Warning: Attempt to present on whose view is not in the window hierarchy!
Thanks in advance
Here is my code for didReceiveRemoteNotification:
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject]) {
var payload = userInfo
let requestID = payload["requestID"] as! String
let rootViewController = self.window!.rootViewController as! UINavigationController
let storyboard : UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let vc : RequestViewController = storyboard.instantiateViewControllerWithIdentifier("RequestViewController") as! RequestViewController
vc.requestID = requestID
let navigationController = UINavigationController(rootViewController: vc)
rootViewController.presentViewController(navigationController, animated: true, completion: nil)
}
Fixed it by changing:
rootViewController.presentViewController(navigationController, animated: true, completion: nil)
to:
rootViewController.visibleViewController!.presentViewController(navigationController, animated: false, completion: nil)

Resources