Why doesn’t 'self.navigationController?.pushViewController(mainPage, animated: true)' work? - ios

I am making a basic app that lets users sign up and login and they will be directed to the main page which has horizontal scrolling and paging enabled. The main view has 3 views which you can swipe left or right to go to the other pages. Here is the code where I set the root view controller.
NOTE: Only if the user has logged in this view controller will be displayed.
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
FirebaseApp.configure()
window = UIWindow(frame: UIScreen.main.bounds)
let homeScreen = MainScreen()
let navBar = UINavigationController(rootViewController: homeScreen)
window?.rootViewController = navBar
window?.makeKeyAndVisible()
return true
}
}
I want to display the main page once the user logs in, but the main page doesn't get displayed. Here is the code when the user taps on the login button, I've only given the necessary code:
// User found and verified, go to main page
let mainPage = MainScreen()
self.navigationController?.pushViewController(mainPage, animated: true)
The pushViewController doesn't work.

Make sure that before you push a viewcontroller your root view controller must be a navigation view controller otherwise it will not work.

thanks for your answers but I figured it out.
// User found and verified, go to main page
let mainPage = MainScreen()
let navController = UINavigationController(rootViewController: mainPage)
self.present(navController, animated: true, completion: nil)

Related

How to set a UITabBarController as rootViewController from Storyboard

Short question:
How can I launch and make a UITabBarController be the rootViewController of my app after starting with a Storyboard?
Long question:
I'm not a swift expert, but I managed to create a complete app using XIBs from the beginning. Now I need my app to start with a Storyboard as a new requirement to post updates to the appstore from 01/07/2020, but I never used it to build my views. It was easy to modify my app to have my Storyboard as an entry point, but the problem is that my initial view today is a TabController, and I don't know how to navigate from my initial Storyboard to my TabController.
My AppDelegate today works something like this:
var window: UIWindow?
func application(_application: UIApplication, didFinishLauchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
self.window = UIWindow(frame: UIScreen.main.bounds)
// initiate UINavigationControllers and UITabBarController here...
tabController.viewController = [nav1, nav2, nav3, nav4]
tabController.modalPresentationStyle = .fullScreen
self.window!.rootViewController = tabController
self.window!.makeKeyAndVisible()
}
All my attempts ended with a white screen after showing my Storyboard without showing my TabBar.
One of these attempts was this:
override func loadView() {
super.loadView()
// initiate tabController the same way I did in the AppDelegate
UIApplication.shared.keyWindow?.rootViewController = tabController
}
Check the value "Is initial View Controller" for it.

Navigation controller turns to view controller when root view is set to different view controller

I have a root view that shows up when I first start my app. The root view has a button that when pressed opens another view controller that should have a nav bar at the top with an (add) navigation item. The problem is when I set the root view as the root view in app delegate, my other controller (that opens from a button) fails to show the nav bar with the nav item.
Heres what I mean:
When App Delegate is set to
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
window = UIWindow(frame: UIScreen.main.bounds)
window?.makeKeyAndVisible()
let firstviewController = FirstViewController()
let navController = UINavigationController(rootViewController: firstviewController)
let core = CoreViewController()
window?.rootViewController = navController
return true
}
The navigation controls and navbar all show up but the CoreViewController (the view with the button) fails to show up first as the root controller.
How can first view controller keep its NavControls while have CoreViewController launch first as the root screen?
View Controller with Button
When View Controller is a Nav Controller

ViewController appears mulitple times when creating a navigationController programmatically in swift

I have been creating my app completely from scratch programmatically, without using Storyboards.
My app is integrated with Firebase, and uses Facebook login.
My setup is fairly simple:
Launch the app -> takes you to the first VC called WelcomeViewController.
There is a check that happens in the viewDidLoad method to see if a user is already signed in and exists. If there is, it sends you straight to the second VC called FilmsViewController
The FilmsViewController is a collectionViewController that displays films. The user can press a film, and it takes them to more information about that film.
(For reference, I am already signed in with Facebook in my app)
I have a current issue, where when step 2 above happens, it transitions to the FilmsViewController, but it does it like 2 or 3 times. So you see the new VC appear like 2 or 3 times, then the content loads. If you press the Back button in the nav bar, it takes you back through the 2 or 3 viewControllers that it loaded before taking you back to the WelcomeViewController.
I have set my views up as follows.
In AppDelegate.swift:
var window: UIWindow?
var navController: UINavigationController?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
navController = UINavigationController()
let firstViewController: WelcomeViewController = WelcomeViewController()
self.navController!.pushViewController(firstViewController, animated: true)
window = UIWindow(frame: UIScreen.main.bounds)
window?.rootViewController = navController
window?.makeKeyAndVisible()
return true
}
In the WelcomeViewController in the viewDidLoad:
FIRAuth.auth()?.addStateDidChangeListener { auth, user in
if let user = user {
// User is signed in.
// Direct the user to the home screen
let toFilmListVC = FilmsViewController(collectionViewLayout: UICollectionViewFlowLayout())
self.navigationController?.pushViewController(toFilmListVC, animated: true)
} else { ...
}
}
I have looked loads for a solution - and nothing. I've only found one post on this issue, where someone said the solution was to change the class name of that controller, which I have already done and it didn't change anything.
Can anyone help me resolve this, please? Thank you.
The addStateDidChangeListener is probably being called multiple times.
You should modify it to check whether a FilmsViewController has already been pushed, to prevent pushing another one:
FIRAuth.auth()?.addStateDidChangeListener { auth, user in
if let user = user {
// User is signed in.
// Direct the user to the home screen
// Only push one FilmsViewController onto the navigation stack!
var shouldPush = true
if let navigationController = self.navigationController {
for viewController in navigationController.viewControllers {
if viewController is FilmsViewController {
shouldPush = false
}
}
}
if shouldPush {
let toFilmListVC = FilmsViewController(collectionViewLayout: UICollectionViewFlowLayout())
self.navigationController?.pushViewController(toFilmListVC, animated: true)
}
} else { ...
}
}
I know the above solutions works, but to make it look extremely simple.
here is the code i wrote in my project.
let someController = SomeController.someController()
if !(self.navigationController!.viewControllers.contains(someController)){
self.navigationController?.pushViewController(someController, animated:true)
}
only push the controller to navigation bar if the controller is not exist.

Attempt to present controller whose view is not in the hierarchy

I am trying to accomplish what I think is a pretty common set of steps:
When my app starts, load a home controller in a navigation controller:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
let window = UIWindow(frame: UIScreen.mainScreen().bounds)
window.rootViewController = UINavigationController(rootViewController: HomeViewController())
window.makeKeyAndVisible()
self.window = window
return true
}
When my app loads check if the user is logged in. If not, present a registration view controller:
func applicationDidBecomeActive(application: UIApplication) {
if ref.loggedIn != nil {
// user authenticated
print(ref.userData)
} else {
// No user is signed in
let registerViewController = RegisterViewController()
if let navController = window?.rootViewController as? UINavigationController {
navController.presentViewController(registerViewController, animated: true, completion: nil)
}
}
}
In my RegisterViewController, provide a button that switches to a LoginViewController:
#IBAction func login(sender: UIButton) {
print("LOGIN")
let loginViewController = LoginViewController()
// How do I present the view controller here?
}
My question is: how can I present the LoginViewController so that calling self.dismissViewControllerAnimated(false, completion: nil) will return to my HomeViewController?
Things I've tried:
If I call self.presentViewController from the RegisterViewController then dismissing returns back to the RegisterViewController instead of HomeViewController
If I try to get a reference to rootViewController via UIApplication.sharedApplication() and present the login controller on rootViewController then I get an error "Attempt to present ... on ... whose view is not in the window hierarchy!"
Thanks!
It's obvious that you will get the error "Attempt to present ... on ... whose view is not in the window hierarchy!"
First you need to make the instance of ViewController using storyboard identifier.
Please try this :-
let storyboard = UIStoryboard(name: "YourStoryboardName", bundle: nil)
let vc = storyboard.instantiateViewControllerWithIdentifier("viewControllerToBePresented") as! UIViewController //use your class name here to cast it.
self.presentViewController(vc, animated: true, completion: nil)
EDIT
In your case you can use protocol on register screen which will get
implemented on home screen.
And in that implementation you can write code to dismiss register view
and then present Login view.
If you are trying to present a new view controller add that view controller in navigation controller and then present the navigation controller..
From the presented view controller you can dismiss to previous view controller.
I hope this will work..
In your case you can use protocol on register screen which will get implemented on home screen.
And in that implementation you can write code to dismiss register view and then present Login view.

Parse Facebook User logged in, perform segue

I am trying to skip over my login view once a user is logged in. How can I check to see if a user is logged in through Facebook while the app is starting?
I currently have the following code in a LoginViewController:
override func viewWillAppear(animated: Bool) {
var loggedIn = PFFacebookUtils.session().isOpen;
if (loggedIn) {
performSegueWithIdentifier("skipLogin", sender: self)
}
}
This does not move to my next view even after the user has clicked the "Log in with Facebook" button.
I get the following error:
Warning: Attempt to persent <_Project.HomeViewController: 0x7fa331d3af00> on
<_Project.LoginViewController: 0x7fa331f08950> whose view is not in the window hierarchy!
As discussed in chat, you have basically two options here:
Let the user "see" the animation from the login view controller to the second one. In that case you should do the push in viewDidAppear instead of viewWillAppear (where the view is not fully prepared, as the runtime warning clearly states).
If you prefer showing the final view controller immediately, without any animation, then it's better to put that logic inside your app delegate, and choose which initial view controller should be loaded from here. In that case, you're not actually performing any segue, you're just assigning one or another view controller to the main window (or your navigation controller).
Parse has the "AnyWall" sample app that implements the second logic. See here for more details: https://parse.com/tutorials/anywall#2-user-management. In particular, the chapter 2.4 is of special interest, as it explains how you can keep a user logged-in.
Simply put, here's how they did it (I've adapted their Objective-C code to Swift):
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
...
navigationController = UINavigationController()
...
// If we have a cached user, we'll get it back here
if PFFacebookUtils.session().isOpen {
// A user was cached, so skip straight to the main view
presentWallViewController(animated: false)
} else {
// No cached user, go to the welcome screen and
// have them log in or create an account.
presentLoginViewController(animated: true)
}
...
window = UIWindow(frame: UIScreen.mainScreen().bounds)
window.rootViewController = navigationController
window.makeKeyAndVisible()
return true
}
In each of the two methods present...ViewController, they use the following skeleton:
func presentxxxViewController(#animated: Bool) {
NSLog("Presenting xxx view controller")
// Go to the welcome screen and have them log in or create an account.
let storyboard = UIStoryboard(name: "Main", bundle: nil) // Here you need to replace "Main" by the name of your storyboard as defined in interface designer
let viewController = storyboard.instantiateViewControllerWithIdentifier("xxx") as xxxViewController // Same here, replace "xxx" by the exact name of the view controller as defined in interface designer
//viewController.delegate = self
navigationController?.setViewControllers([viewController], animated: animated)
}
The navigationController and window vars should be defined like this in AppDelegate:
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var navigationController: UINavigationController?
...
}
If your app also uses a navigation controller as its root view controller, you can probably use the same code.

Resources