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)
}
Related
So, I want to prefetch some of the data needed in the view controllers associated with the tab bar controller as the user moves from the login page to the home page (tab bar controller exists between these two view controllers). I'm fetching the data in a custom TabBarController class and using the following code to send it (doesn't work):
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.destination is Profile { // Profile = one of the view controllers in the tab bar
let vc = segue.destination as? Profile
vc?.uid = self.userID
vc?.name = self.name
vc?.skills = self.skillSet
}
}
What is the best way to do this? Please note I can't prefetch the data before the tab bar controller as that is the login page. If I prefetch the profile page data on the home page (tab bar controller {home, search, profile}), how can I transfer it to another view controller (to profile) in the same tab bar?
What is the best way to do this?
I believe once you perform login call (Inside login screen) you will have userID and other data related to that user, so inside Login controller you have added code for making Tabbar controller as initial controller.
So based on that best thing is to store userData into tabbar controller.
So once you sore data into tabbar controller (from either login or Home Screen) you can fetch user data in any root controller (home, search, profile) which is inside tab-bar by below code.
struct User { var userID: String? }
class TabbarController: UITabBarController {
var userData: User
}
class HomeController: UIViewController {
var userData: User?
override func viewWillAppear(_ animated: Bool) {
if let tabBar = self.tabBarController as? TabbarController {
self.userData = tabBar.userData
}
}
}
There are multiple ways you can achieve this.
Using SuperViewController,
Using SingleTone class if you want to access in other than TabbarViewVontroller subclass.
Saving in a common location like in constants if data is small.
Prefetching data in one VC e.g Home and then passing to the required VC on click.
I'm facing a slight problem with my UINavigationBar.
I have a an Initial Root View Controller (ViewController) followed by 2 View Controllers (LoginMRNViewController and LoginOTPViewController) that are used to login the user with One Time Password. I also have NavigationController used to navigate the user to his/her homepage after logging in.
In the homepage, I have a (Logout) button, that logs out the user with Firebase and navigates him/her to the Initial Root View Controller (ViewController).
The UINavigationBar works like a charm, however, whenever the user logout, he is navigated to the Initial Root View Controller (ViewController) but the UINavigationBar completely disappears!
My logout function:
#IBAction func logoutPressed(_ sender: Any) {
do {
try Auth.auth().signOut()
self.performSegue(withIdentifier: "goToLoginScreen", sender: self)
print ("User logged out")
} catch let error {
print ("Failed to logout with error", error)
}
}
Here's how my Storyboard looks like.
EDIT:
I tried to put this in my Initial Root View Controller (ViewController) and the other 2 View Controllers (LoginMRNViewController and LoginOTPViewController) in the ViewWillAppear method, but unfortunately it did not work.
self.navigationController?.setNavigationBarHidden(false, animated: false)
tabBarController?.tabBar.isHidden = false
Here's how my updated Storyboard look like.
Don't add the another NavigationController to hide back button.
Add navigationItem.setHidesBackButton(true, animated: true) to hide back button.
Then on logout simply add self.navigationController?.popToRootViewController(animated: true)
It should work as expected.
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
Pretty new to Swift so forgive my ignorance. I have a navigation controller which is linked to a View controller called Profile. In ProfileViewController.swift I have it to where it opens up another view controller if no profile exists. Here's an example of what I have at the moment:
// ProfileViewController.swift
var profileExists = 0
override func viewDidLoad() {
super.viewDidLoad()
// Check if user has a profile. If so, go to view page; otherwise, bring up new profile view.
if profileExists != 1 {
// Create new profile page.
if let vc = storyboard?.instantiateViewController(withIdentifier: "EditProfileController") as? EditProfileViewController {
present(vc, animated: true, completion: nil)
}
print("Need to create new profile.")
}
//Scroll view size
ScrollView.contentSize.height = 1000
}
// Cancel button was pressed.
#IBAction func unwindToHomeScreen(segue:UIStoryboardSegue) {
}
What I'm trying to do is link my "Cancel" bar button in the "EditProfile" view controller to dismiss it if the user clicks cancel. However, trying to replicate an example from a tutorial isn't working. I can't control + drag the Cancel button to the Exit button and use a defined unwind segue. Not quite sure why just yet.
Any ideas on the best way I should be trying to dismiss this view controller?
Simple call
self.dismiss(animated: true, completion: nil)
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