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)
Related
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)
I have a root view controller - which is a simple page presented to the user. In this page, there is a UIBarButton that goes to the user profile page.
When the user is in its profile, he/she'll have a back button that will allow him/her to go back to the root view controller.
However, the root view controller is different for admins and clients (there is one standard view controller for admins and one standard view controller for clients).
How can I create two segues that are triggered by the 'Back' profile button? I mean, depending on who is currently logged in (an admin or a client), I would like to go back to their respective pages.
The client cannot go back to the admin's page and neither vice versa.
Is there any way to create these two segues? Or even another/better way? I have tried to do that using storyboard, but when I create the second segue, it deletes the first one linked to the button.
You cannot do that but there is other way for that, create two segue bwtween SenderViewController and DestinatrionViewController. After that set action for your button and perform the segue according to your requirements.
#IBAction func buttonAction(_ sender: UIButton) {
if condition {
self.performSegue(withIdentifier: "identifier1", sender: nil)
}
else {
self.performSegue(withIdentifier: "identifier2", sender: nil)
}
}
Going off of Nirav D's answer, use an if...else with the condition that they are an admin or client. But don't control+drag from the button itself, control+drag from the ViewController as seen in the images below.
you should handle it in your code.
#IBAction func backClick() {
if isAdmin /* check condition here */ {
// admin work
} else {
// normal user work here
}
}
Then let create segue from storyboard and give it an identifier.
If you want perform segue, let use
performSegue(withIdentifier: "identifier", sender: nil)
if you want go back.
dismissViewControllerAnimated(true, completion: nil)
Hope it help.
i'm building an app for which i need a split view controller. I embedded it in a container view so i could use a segue to acces it. The problem now is that I can't use my navigation bar in a proper way anymore. Normally, there should be a button to go to the previous page, but now I only have the option to go to the homepage (for within the split view controller). You can find some images here. Also, the title will always be "Shoppen", even though I stated it otherwise.
I suppose the problem is that the navigation bar of the view controller in which the container view is set will always be the top one. I have no idea on how to either delete or fix this problem though, could anybody here help me with this? Thanks in advance!
Some code to show you what it should normally do:
In the first section, the table view, it should indeed always title "Shoppen" and able to go back to the homepage
In the detail section, we should be able to go to the table view page, and the title should be the category of the shop we clicked. This is defined here:
func configureView() {
if let detailWinkel = detailWinkel {
if let detailDescriptionLabel = detailDescriptionLabel, WinkelImageView = WinkelImageView {
if detailWinkel.name == "Hunkemoller" {
detailDescriptionLabel.text = "Hunkemöller"
} else {
detailDescriptionLabel.text = detailWinkel.name
}
WinkelImageView.image = UIImage(named: detailWinkel.name)
title = detailWinkel.category
StraatDescriptionLabel.text = detailWinkel.straat
AdresDescriptionLabel.text = detailWinkel.adres
WebsiteButton.setTitle("Open website in Safari", forState: UIControlState.Normal)
}
}
}
I don't completely understand what you are trying to do.
An embed segue is not a normal segue. You can't push a view that is embedded into another view controller onto a navigation stack, if that's what you are saying.
An embed segue is a degenerate case. It invokes the contained view controller at the time the parent view controller is loaded, and there is no mechanism for going back.
In Swift 2, Xcode 7.1
As the image above, TabBarController will be the main view. Anonymous user can go to the home tab, if they click on the "Me" (Profile) tab, I want the app will pop up the new login segue (navigation controller). User need to login 1st before they can go into the "Me" tab.
Currently I have override the TabbarControllerDelegate in the TabBarController class, which as below (I use Parse, so user = PFUser.currentUser(), Parse will cache the user):
func tabBarController(tabBarController: UITabBarController, shouldSelectViewController viewController: UIViewController) -> Bool {
if (viewController is MeViewController && user == nil) {
print("login \(viewController)")
// How could I go to the Login Segue from here?
return false
} else {
return true
}
}
How could I achieve my goal from here? Or any better advice? Btw, I am not able to use performSegueWithIdentifier with the TabBarController class unless I use it within the ProfileViewController class (UIViewController) to achieve it. But if I did that, the app will display the view in 'Me' Tab first then go to the 'Login' segue, which is not what I wanted
It will be great thanks for any advices
If you want the user to not be able to go to me before login, you can just do a check with a static bolean which will be initialize in app delegate.
Then if the bolean is set to false (default value), you can invoke the your login popup.
No point here to perform a segue, just put an identifier on the view in the storyboard (identity inspector => storyboard id) and then you can invoke this particular view of the storyboard like this :
let SB = UIStoryboard(name: "MyStoryboard", bundle: nil)
let controller = SB.instantiateViewControllerWithIdentifier("MyId")
self.presentViewController(controller, animated: true, completion: nil)
Segues are only for views which are linked to each other, i personnaly almost never used them.
I recommend that you make the login UINavigationController segue come directly from the UITabBarController instead of on the "Me" controller. This is because you can't modally present your login controller through the "Me" controller if its not on the screen.
Give the segue an identifier (eg. "my-identifier"):
Click on the segue in the storyboard editor and fill in the identifier field in the right sidebar.
After this is configured correctly, the following code will perform the login segue (note the use of tabBarController since you should have the segue configured from the UITabBarController:
tabBarController.performSegueWithIdentifier("my-identifier", sender: self)
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