I have 2 view controllers. Profile and SignIn. Actually Profile is root view controller. And When I press sign in button, signInViewController dismiss. How to update or refresh profileViewController after this?
I tried something like this:
override func viewWillAppear(animated: Bool) {
self.navigationController?.navigationBar.topItem?.title = "\(name)"
}
but this didn't help
You can user protocols to pass data between viewControllers, to do that you can refer this question
Related
I have a dashboard, then a login screen and profile screen. Once I reach profile screen I want login screen to be removed so that when I press back button, I am taken to dashboard instead of login. All these are view controllers of a navigation view controller and I display tge using present method.
I have a senario where login can appear anywhere during using navigation. Example: Dashboard, Screen 2, Screen 3, Login, Profile. Here, I have to remove login and when user taps back from profile, it should display Screen 3.
If you are logged-in already and you have to move to top viewController you can do
window.rootViewController.dismiss(animated: true, completion: nil)
But If you are using navigationController then this will work:
navigationController.popToRootViewController(animated: true)
Implement the following in your login view controller:
override func viewWillDisappear(_ animated: Bool) {
var navigationArray = navigationController?.viewControllers
let count = navigationArray?.count
navigationArray?.remove(at: count! - 2)
navigationController?.viewControllers = navigationArray!
}
This removes the current view controller(the login VC in your case) from the navigation stack just when it is about to disappear. So when you tap the back button from the next VC it will always take you to the VC preceding the login.
This is generic answer, not for the answer of this question only.
When popping, you may kick out the viewControllers from your navigation Controller, would solve your problem
extension UINavigationController {
public func removeViewController(classes : [String]) {
var vcs = [UIViewControllers]()
for viewController in self.viewControllers {
let name = viewController.className
if !classes.contains(name) {
vcs.append(viewController)
}
}
if classes.count < vcs.count {
self.viewControllers = vcs
}
}
}
now think you have 3 viewControllers , dashboard, login, profile. you want to remove login and Move Back from profile to dashboard
In profile's View Controller
override func viewDidLoad() {
super.viewDidLoad()
//your works
let viewControllersToRemove = [String(describing: type(of:login))]
navigationController.removeViewController(classes : viewControllersToRemove)
}
This may sound very basic. But I couldn't find a way out.
I'm using firebase authentication if this is necessary to know beforehand.
The initial view controller is set as LoginViewController. I've two buttons here: Signup & Login. The Signup button presents another view controller modally where user can opt for signing up. After successful sign-up, the SignUpViewController is dismissed, so the LoginViewController appears. Inside the LoginViewController's viewDidAppear(_:) I check for current user and if available redirect user to HomeViewController:
class LoginViewController: UIViewController {
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if let _ = Auth.auth().currentUser {
// This is a modal segue
performSegue(withIdentifier: "SegueToHomeViewController", sender: self)
}
}
}
When tapping Logout button in HomeViewController, the controller is just dismissed with signing out user.
class HomeViewController: UIViewController {
#IBAction func logoutButtonDidTouch(_ sender: UIBarButtonItem) {
do {
try Auth.auth().signOut()
// This dismiss lands user to the LoginViewController again
// So user has chance to login again with `Login` button this time
dismiss(animated: true, completion: nil)
} catch {
print(error)
}
}
}
Everything is working fine so far. But there is a subtle misbehavior. Because every time the app is launched the LoginViewController appears and then redirect to HomeViewController which isn't so good as user experience.
So, what I want is if the user is logged-in already the first view s/he will see is HomeViewController other than LoginViewController. But if user didn't log in before, s/he will be landed on the LoginViewController.
Yes, I could check from the AppDelegate's application(_:didFinishLaunchingWithOptionss:) for current user and if found, navigate directly to HomeViewController. But then I would lose the LoginViewController when user taps on Logout in HomeViewController.
At a glance this is pretty much what I need to accomplish:
Anything even without the code example is appreciated.
try to check in loadView() function , or you can create an entry view controller with background image like splash screen , make it your initial view and set your check func in viewWillAppear of it's class
I had a similar dilemma, and solved it by adding a 'Splash' ViewController as the entry controller for the App. I have styled it identical to the LaunchScreen.storyboard. In the SplashVC I check for a valid User Token and if present it will send direct to the main app (in my case this is a TabBarController). In my TabBarController I set a Firebase addStateDidChangeListener which will direct back to the LoginVC if the User Token ever becomes invalid.
I also use the Splash VC to run async code to check subscription status, and write login details to Firebase.
Please excuse the quick and dirty schematic.
There is no lose when you logout you can easily show any VC anyTime without a segue , jsut give every viewController an identifier and set it to UIApplication.shared.keyWindow?.rootViewController
let loginView = self.storyboard?.instantiateViewController(withIdentifier: "loginView") as! loginVC
let nav = UINavigationController(rootViewController: loginView)
nav.navigationBar.isHidden = true
UIApplication.shared.keyWindow?.rootViewController = nav
I have two VC's in navigation controller - SignInVC and SignUpVC.
Both contains custom google sign in button.
When app launches, SignInVC appears first. When I push SignUpVC and go back the SignInVC in the following way
_ = self.navigationController?.popViewController(animated: true)
I can't run sign in using button because the following
Warning: Attempt to present SFSafariViewController: 0x157d47140 on
TestProject.SignUpViewController: 0x157d2f3b0 whose view is not in
the window hierarchy
error shows up in console.
It's worth to mention that I have also FB sign In button and it works in both view controllers. Also, button in SignUpVC works always, regardless of going back and forth in navigation
Is it something specific to Google Sign In?
How to fix it?
It's because delegates were set in ViewDidLoad() method, which is not called when user gets back to previous controller in navigation stack.
this
override func viewWillAppear(_ animated: Bool) {
GIDSignIn.sharedInstance().uiDelegate = self
GoogleManager.shared.delegate = self
}
solves the issue.
I am working in storyboard and also programmatically do some things. First, I have created a viewController controller which is login page (first view) programmatically. But in storyboard I have a NavigationController whose root is ViewController. Everything (methods forgotPassword and loginDidFinish) worked fine, except that ViewController was viewed before controller immediately after launching the app.
So I have changed the root of NavigationController to controller, and after that my functions does not work. I've tried several things like deleting navcontrol in storyboard, etc. You can see my project here: https://github.com/ardulat/SPE
I will provide you a basic example of a Login scenario, hope it can help you with your issue, what I would do first is set right the navigation between ViewControllers like this:
I have two view controllers in my project (LoginViewController.swift and MainViewController.swift):
So in my storyboard I create two ViewControllers, the first one embedded with NavigationController then I set a segue from my first ViewController to my second ViewController:
Then I give a name to the segue I created like so:
And in order to navigate from Login to Main ViewController, I call the performSegue method inside the loginButtonTapped action that is triggered when the login button is touched.
LoginViewController.swift:
class LoginViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func loginButtonTapped(_ sender: AnyObject) {
// TODO: validate your login form
performSegue(withIdentifier: "LoginToMain", sender: nil)
}
}
So, I have Navigation Controller. there are segue from Root View Controller to other View Controller.
When I want to get access to other View Controller I override prepareForSegue method and use destinationViewController property.
But that's not ok for me. All my stuff in prepareForSegue will be execute every time when segue is called, but I don't want it. Secondly, it destroys logic of my code: after performSegueWithIdentifier(actually before) execution jumps to other place in code.
It would be great if I can get access to other View Controller like I did it with Root ViewController - by keyword self, for example.
That's code example to make my question more clearer:
func startWorking() {
/*here we made some stuff for current VC
...
...
*/
//next we go to new View Controller
performSegueWithIdentifier("newVC", sender: nil)
//then all actions that I want to do begin at another method - prepareForSegue
//But I want get access to View Controller that user sees now!
//For example present some view:
let someView = UIView(frame: someFrame)
/*question subject*/.view.addSubview(somView)
}
/question subject/ - is the current ViewController that I have presented by segue and point of my question.
Sergey Gamayunov,
You can always access the top mostViewController in navigation stack using,
let viewCOntroller = self.navigationController?.topViewController
EDIT
I believe if you cant get your logic around the prepareForSegue or self.navigationController?.topViewController you must take a look into your design pattern :)
That being said I understand all you want to do is to access the ViewController after performSegue without using prepareForSegue, you can use this code
func navigationController(navigationController: UINavigationController, didShowViewController viewController: UIViewController, animated: Bool) {
if viewController is YourDestinationViewControllerClass {
print("You have access to viewController loaded do whatever you want")
}
}
The function stated above is a navigation controller delegate :) So you will have to declare your viewController to confirm UINavigationControllerDelegate. like
class ViewController: UIViewController,UINavigationControllerDelegate
and in
override func viewDidLoad() {
super.viewDidLoad()
self.navigationController?.delegate = self
}
Thats it you are good to go :) Happy coding buddy :)