Right way or event to choose what view load in swift - ios

I'm working in an app that logs in an user if there isn't another user already logged in at launch time. This way the first view to appear should be the Login View. But in the case there is a logged user already, the first view appearing should be the main menu. Im handling this with the viewWillAppear function and it's working, but I don't know if this is the correct approach or how it should be handle in this situations.
Here is my code. My first view is MainMenuVC in which I control if there is a logged user or not, then I choose if stay in main menu view or push my login view.
class MainMenuVC: UIViewController {
override func viewWillAppear(animated: Bool) {
if (UserMgr.users.count == 0){
var vc1:LoginVC = self.storyboard?.instantiateViewControllerWithIdentifier("LoginView") as LoginVC
self.navigationController?.pushViewController(vc1, animated: false)
}
else
{
//I do nothing so this view is loaded
}
}
I don't know if i should use another ViewController and implement the function loadView() to decide what view load, but the problem is make that view work with the story board and my navigation controller.
Any suggestions?

Basically you will have two different view controllers, one for the login screen (VCLogin) and one for the main menu (VCMainMenu). Now, in your AppDelegate there are methods which are called, when the app launches respectively when it appears. So, place the code checking whether a user is logged in there and make the appropriate view controller the root view controller, e.g.
let navigationController = window.rootViewController as UINavigationController
navigationController.rootViewController =
userIsLoggedIn ? mainMenuViewController : loginViewController

Related

Ios navigation - custom back button or removing viewcontriller from stack?

I'am new to ios. I have several view controllers and I want back button to get user to level up controller. Example.
But if user comes from gameOver view, back button sent him back to gameOver and I don't want such behavior (I want user to be sent at second level (games level) controller as shown). On android I could set the pop behavior for the navigation actions with mouse very easily.
What is the correct way to do the same in ios? Or I have to create the custom back button and do everything manually?
Using Swift this can be achieved using below code:
func popToViewController(_ specificViewController: Swift.AnyClass) {
let viewControllers = self.navigationController!.viewControllers
for eachViewController in viewControllers {
if eachViewController.isKind(of: specificViewController) {
self.navigationController!.popToViewController(eachViewController, animated: true)
break;
}
}
}
Usage:
self.popToViewController(gameLevelViewController.self)

IOS swift UIBarButtonItem action

I've a table view with navigation controller embedded in. I've added a UIBarButtonItem (add) button. When I click this button it opens a new view where user enters the data and submits it (which makes a web service call) and returns back to the previous view. This navigation happens as shown below,
func addTapped(_ sender:UIBarButtonItem) {
print("Called Add")
let vc = (storyboard?.instantiateViewController( withIdentifier: "newNote")) as! newNoteVC
self.navigationController?.pushViewController(vc, animated: true)
}
And in new view I do following,
#IBAction func saveButton(_ sender: UIButton) {
if (self.noteDescription.text?.isEmpty)! {
print("Enter missing note description")
return
} else {
let desc = self.noteDescription.text
self.uploadNote(noteText: desc!, noteDate: self.dateInMilliseconds)
self.navigationController?.popViewController(animated: true)
}
}
This way a record gets saved and a view gets popped from the navigation controller stack but only thing I don't how to do is refresh the table view data in the parent view (where I might need to make a new http service call to read all the records).
I hope I'm able to explain the issue? Any help is appreciated.
As mentioned in the comments, making a service call just to update the tableview might be a overkill. However, if this is the business scenario which needs to be implemented, you can do the same in
func viewWillAppear
in the parent view controller. You can make the service call in this method and reload the table view with the data.
You would also need to check the overall navigation of the application as making service calls in viewWillAppear method is not a good approach as these gets called everytime the view is shown. For Ex: If coming back from a navigated view then also the method is called.

UINavigationController popToRootViewController method cannot be called by a delegate?

I have a UINavigationController which works great. Each view controller has its own button that pops the stack back to its root which also works great. However, I'd like to also be able to pop the stack back to its root by pressing a button on the tab bar (which is obviously in an entirely different class outside of the navigation stack).
Therefore, I created a delegate in the tab bar class which finds the view controller at the top of the stack and calls the method in that view controller to pop the stack back to the root. I printed something to the console to verify that the delegate is set up correctly and it is. Everything works exactly as it should, except that pressing the tab bar doesn't pop the stack back to its root.
Thoughts?
This is the view controller at the top of a UINavigationController stack
class BlankViewController202: UIViewController, MainContainerViewControllerDelegate {
// pop to root
func popToRoot() {
self.navigationController?.popToRootViewController(animated: true)
print("success")
}
}
When this function above is called from within the view controller (when the user presses the button on the view controller itself), it pops the stack. But when this same exact method is called by a delegate from the tab bar, it doesn't pop the stack (but it does print to console so I know its hooked up properly).
This is where the button resides in the tab bar that when pressed should pop the stack back to its root
protocol MainContainerViewControllerDelegate {
func popToRoot()
}
class MainContainerViewController: UIViewController {
func moveToTab3(sender: UIButton!) {
// ...
let banana = BlankViewController202()
self.delegate = banana
delegate?.popToRoot()
}
}
The problem is that BlankViewController202() makes a whole new, separate BlankViewController202 — it is not the particular BlankViewController202 that is already in the interface as part of the navigation controller interface. It is that BlankViewController202 you want to talk to.
I think, you error delegate pattern. You can see again model delegate use protocol. If you use protocol, you delete line code "let banana = BlankViewController202()".
Just Follow some steps
1) Make one Object of UINavigationViewController in AppDelegate and you can access it with shared object of app delegate.
2) The first line of moveToTab3 will be [Appdelegate sharedObject].navigationViewControllerVariable = self.navigationViewController
3) In Your delegate method write this line
[[Appdelegate sharedObject].navigationViewControllerVariable popToRootViewController:true]
this will work definitely :)

How to manage loginViewControllers in an app programmatically?

I am trying to use Firebase to create a mini social media. There are currently two components, a loginRegisterViewController, which handles login and registration, and a TabbarViewcontroler, which presents the main content.
In appDelegate's didFinishLaunching method, i set the TabbarViewcontroler as the rootView controller
let rootviewController = TabbarController()
window?.rootViewController = rootviewController
and in the TabbarController's viewDidLoad method, i implement the following code to see if a LoginViewController should be presented or not.
class TabbarController: UITabBarController {
var handle : FIRAuthStateDidChangeListenerHandle?
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
// check if the user is logged in or not.
handle = FIRAuth.auth()!.addStateDidChangeListener(){ auth, user in
if user == nil {
self.present(loginRegisterViewController(), animated: true, completion: nil)
}else{
self.setupTabbarController()
}
}
}
}
If the user is logged in, we setup the tabbarViewController, if the user is not, we present the loginRegisterViewController on top of the tabbarViewController.
However, every time I register a new user and dismiss the loginRegisterViewController, I will get an empty tabbarViewController.
I understand that this happens because of the if-else condition in the viewDidLoad method of the tabbarViewController.
Since initially there is no user logged in, the
self.setupTabbarController()
method is thus not called.. Therefore, after i register a new user and dismiss the loginRegisterViewController, i get an empty tabbarViewController.
But, how do I go to solve this? Or is there a better way of checking if a user is logged in or not to present the mainContentViewController or LoginRegisterViewController?
Thanks in advance.
Edit : The setupTabbarViewController is a function to setup the tabbarController
func setupTabbarController(){
let shopControler = ViewController()
let shopNavigationControler = UINavigationController(rootViewController: shopControler)
shopNavigationControler.tabBarItem.title = "Shop"
shopNavigationControler.tabBarItem.image = UIImage(named: "Shop_Tabbar_Image")
viewControllers = [newsfeedNavigationControler, shopNavigationControler]
}
as you mentioned correctly we would need to call self.setupTabbarController() again, after the login state changes.
I usually go with this kind of implementation:
Create a class AuthManager or something like that. This has the users loginState and a function called configureRootViewController.
Declare a static sharedInstance.
In the AppDelegate - didFinishLaunchWithOptions:
Your call would look something lie this:
AuthManager.sharedInstance.configureRootViewController()
All that configureRootViewController() does is it checks the login state and configures the correct rootViewController.
Every time your login state changes you want to call this method again.
I hope that helped. Still rather early to give precise answers. :-)
Have a great day.

How to add a tabbar after a user action

I have an app that doesn't have to show any tab when the user isn't logged in. I'm facing many issues with this functionality. First of all, I had embed the views, after the login screen, into a tab bar controller and everything was showing ok, except when I had implemented the login feature. As it's an async call, I had to wait until the credentials were validated. I wasn't able to do this in the shouldPerformSegue method or any other method provided by apple because you can't block the main thread until the async stuff is done, so the segue has to be done programaticaly in an IbAction:
#IBAction func doLogin(sender: AnyObject) {
userIsLogged = false
let apiCall = webApi()
apiCall.callCheckIsUserLogged(nil, password : self.passwordField.text, email: self.mailField.text){ (ok) in
if ok {
if(userIsLogged == true){
dispatch_async(dispatch_get_main_queue()) {
self.performSegueWithIdentifier("loginUser", sender: self)
}
}else {
NSOperationQueue.mainQueue().addOperationWithBlock{
print("User not logged in")
self.alert.message = "Please enter valid credentials"
self.displayAlert()
}
}
}
}
}
But this has driven me to another issue: after the programatic segue, my tab bar controller was disappearing, and after read a while it looks like the only way to avoid this is to embed your tab bar controller into a navigation controller. So I did it, but now, I got many new issues. First of all I got two navigation controllers, the one who is at the very beginning of the project and this new one I have embed into the tab bar controller. A picture will illustrate this better than my words:
Now I have two navigation controllers, and I don't know how to hide the top one. Already tried:
self.navigationController?.navigationItem.hidesBackButton = true
But is hiding the arrow and I need to hide the other one navigation controller. But the best thing indeed would be to see the best approach for this kind of cases, when you want to add a tabBar controller embed into a navigation controller in the middle of the project.
Thanks all
I guess you can take another approach. Make login storyline and your app storyline distinct.
Have a storyboard for your login procedure, and another storyboard for your home (or whatever you like) and manage them in AppDelegate.
This is how i did it:
if /* user must log in */ {
self.window?.rootViewController = loginStoryboard?.instantiateInitialViewController()
self.window?.makeKeyAndVisible()
}
else {
self.window?.rootViewController = homeStoryboard?instantiateInitialViewController()
self.window?.makeKeyAndVisible()
}
Put this code in a method (for example called manageRootViewController()) and call it at app launch, or after your login. (You can also add custom animations if you like)

Resources