I have an application that can be used only if the user is authenticated. In particular, I created two different UIViewController. The first is called LoginViewController while the second is called HomeViewController. In applicationDidFinishLaunching: method, LoginViewController is created and then added to rootViewController property like this:
LoginViewController* loginCtr = ... // alloc and initiWithNibName...
self.window.rootViewController = loginCTr;
[loginCtr release];
Whitin LoginViewController I created a method that performs the login. When the user has been authenticated, I perform a method, called performLogin.
- (void)performLogin
{
MyAppDelegate *appDelegate = (MyAppDelegate *)[[UIApplication sharedApplication] delegate];
[appDelegate switchView];
}
where swicthView method has been implemented inside the Application delegate class.
- (void)switchView
{
if(VIEW_TYPE == kLogin) // Display Login
{
// create a new LoginViewController and assign it to rootViewController
}
else // Display Home
{
// create a new HomeViewController and assign it to rootViewController
}
}
Given the previous code, is it possible to implement a more elegant mechanism to manage login / logout transition or does this type of implementation could be considered a valuable solution?
Thank you in advance.
Another option, I will set the HomeViewController as the rootViewController. In the viewDidLoad or viewDidAppear method (before I display any information on the HomeViewController), I will check whether the user has login credential. If not, I will display the loginViewController as a modal to request user login credential. In this case, I don't need to change the rootViewController of the window.
Starting from iOS 5 I started to manage login/logout transitions using the new containment API for UIViewControllers.
Implementing a Container View Controller
If iOS 5 is not available I would see a similar approach writing-high-quality-view-controller.
Hope that helps.
Related
In my chatting application, I have a ChatViewController.m that allows users to message with the QuickBlox framework.
When a user sends an image, a background upload begins and a UIProgressView displays the progress of the upload.
But what if the user backs out of that view during the upload, and returns in, say, 10 seconds while the upload is still happening. I want the UIProgressView to still be active and accurate based on that upload. But dismissing the ViewController doesn't allow me to do that.
Can someone suggest what I should be doing in this situation?
EDIT: This is how I present the ChatViewController.m, depending on the chat selected from the CollectionView:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if([segue.destinationViewController isKindOfClass:ChatViewController.class]){
ChatViewController *destinationViewController = (ChatViewController *)segue.destinationViewController;
if(self.createdDialog != nil){
destinationViewController.dialog = self.createdDialog;
self.createdDialog = nil;
}else{
QBChatDialog *dialog = [ChatService shared].dialogs[((UICollectionViewCell *)sender).tag];
AppDelegate *appDelegate = (AppDelegate*)[[UIApplication sharedApplication] delegate];
appDelegate.dialog = dialog;
}
}
}
EDIT 2: I have implemented the ViewController as a singleton in my didSelectItemAtIndexPath. But now, the app presents only a black screen.
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
QBChatDialog *dialog = [ChatService shared].dialogs[indexPath.row];
ChatViewController *chatView = [[ChatViewController alloc] init];
chatView.dialog = dialog;
[self presentViewController:chatView animated:YES completion:nil];
}
You should change the way you display your chat view controller. Don't use a segue.
Instead, set up your chat view controller as a singleton. Set up an IBAction or other code triggered by the user selecting an item in your collection view.
In that IBAction or didSelectItem code, fetch a reference to your singleton, configure it as needed, and present it modally yourself using presentViewController:animated:completion:
That way your view controller will preserve it's contents between views.
As the other poster said in a comment, you might pull the download logic out of your view controller and into a separate download manager class. It depends on whether you need the ability to do asynchronous downloads in places other than your chat view controller.
I'm assuming based on your question that you are creating the view controller each time that you are presenting it. Instead of re-allocating and creating a new view controller each time you present the messaging view, make only one view controller that you can call and present from any other view controller.
A couple possible ways to do this are:
Create a singleton that has the messaging view controller as a property
Add the messaging view controller as a property on your route view controller
Make the messaging view a singleton itself so only one gets created in the entire life of the application
Doing any of these will make sure that the view persists each time that it's dismissed.
If that still doesn't fix the problem, you may be resetting the view in viewDidLoad or viewDidAppear which I don't think is actually what you want to do.
I am building an app using the Parse Library and trying to integrate Twitter auth. As Parse caches if a user is already logged in using
if ([PFUser currentUser])
I would like to show a different UIViewController depending if they are already logged in.
What is the best practice for this in iOS development, should I perform the checks in appDelegate or in some NavigationController.
How would I perform this in code?
The one of the solution is that you can add one view controller (root VC) as a home page where you check if the user is logged and base on the value you can call performSegue... method with different identifier depends on the value.
It will require add two view controllers in storyboard one for logged and one when the user is not logged and create two segue from the home VC to those views. You need to also named the segue differently, you will be passing the name to the performSegue... method.
I would lay out your storyboard with segue in the following way;
navController->loginController->mainController;
Assign an identifier to the mainController in Interface Builder. we'll call it mainVC for the purpose of this exercise. In viewDidLoad for the loginController, you can validate the user and transition to the mainController if the user is already authenticated.
E.G.
- (void)viewDidLoad{
[super viewDidLoad];
/// validate authentication status
if([PFUser currentUser]){
UIStoryboard *storyBoard = self.storyboard;
UIViewController *targetViewController = [storyBoard instantiateViewControllerWithIdentifier:#"mainVC"];
UINavigationController *navController = self.navigationController;
if (navController) {
[navController pushViewController:targetViewController animated:NO];
}
}
}
This will display the mainViewController without stopping for authentication.
I have a question about iOS Storyboard.
In my storyboard I have my flow:
Navigation Controller;
Login View Controller;
Detail View Controller;
This is the regular flow if there isn't a valid user session.
When I start the app I need to check if there is a valid user session. In this case I need to visualize directly the 3° (Detail View Controller)
I'm not sure but I think that I need to make the check about user session in my Appdetegate. How can I load, in this case, directly the 3° View Controller?
SOLUTION 1
I think what you can do is not check a valid session at all. Here is what I think
1) Create a BOOL called isUserSignedIn that is initially set to NO and save that in you NSUserDefaults. (AppDelegate didFinishLaunchingWithOptions method)
2) Once user makes a valid Login set the BOOL to YES. Save BOOL in NSUserdefaults
3) When user logs out set BOOL to NO
4) Everytime the user opens the app. In AppDelegate's didFinishLaunchingWithOptions check of the BOOL is yes or no. if no loads LoginViewController. if yes do nothing.
This is how I'd do it. Always save every BOOL changes in NSUserdefaults and loads from there.
SOLUTION 2
But if you want to load your DetailViewController in your AppDelegate use this code:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
//check your login session here…
//if session ok loads DetailViewController
DetailViewController* detail = [[DetailViewController alloc] init];
[self.window.rootViewController presentViewController:detail
animated:NO
completion:nil];
//if not
// do something..
return YES;
}
I'm in front of a little problem :
I have something like that when launching the app :
(1) Splash view --> I am logged ? --> (3) HomeView | else (2) Login view
So when I log, then I go to the home view. And when I logout, I can unwind to the login view because i came from it.
BUT if I don't pass from the login view and redirect directly to the home view, I can't unwind to login view when logout.
Someone know a solution about this ?
I just put my logic here:
Take/add one viewController such like, DummyViewController as rootViewController of your app.
in the DummyViewController's viewWillAppear method put logic like a
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
if(login == YES)
{
//go to home screen
}
else
{
// go to login screen
}
}
In DummyViewController you just need to write code in viewWillAppear not anymore.
If you want to Logout then just call
[self.navigationController popToRootViewControllerAnimated:YES];
And viewWillAppear method of DummyViewController will manage your screen based on login status.
You can use NSNotificationCenter to notify your root class when logout done. Then pop to your rootViewController
If you are using UINavigationController, Just present your login controller on NavigationController's RootViewController as like below
-(void)logoutNotification
{
logout = YES;
}
-(void)viewDidAppear:(BOOL)animated
{
if (logout)
{
AuthController * auth = [[AuthController alloc] init];
[self presentViewController:auth animated:NO completion:^{
}];
logout = NO;
auth = nil;
}
}
Maybe is a bit dummy way to do it, but you can simply ALWAYS load the login view and delegate to it the "I'm logged?" check. You can load it hidden or with a waiting sign or whatever... This way you have it already loaded when you logout.
Without seeing your code I can't show you how to, but I guess the logic is enough.
I'm trying to build the sample app from facebook iOS SDK but I'm using storyboards so it's a little different.
I'm doing this check in my AppDelegate.m application didFinishLaunchingWithOptions:
if (FBSession.activeSession.state == FBSessionStateCreatedTokenLoaded) {
// To-do, show logged in view
} else {
// No, display the login page.
[self showLoginView];
}
return YES;
I've created a segue from my main ViewController to a navigation controller. The segue works fine if I trigger it using a button in my ViewController view, but I can't call that method from my AppDelegate.
The name of the method(that performs the segue) is -displayLoginView but I can´t find a way to point to the particular instance of my ViewController that is active at launch.
Any ideas?
Your UIApplicationDelegate is not a view controller so it cannot initiate any segues.
You probably want to call self.window.rootViewController to get the viewcontroller in question but it might be easier to put that code in your viewController's viewDidAppear or viewWillAppear method instead of the app delegate.