How to presentModalViewController in applicationDidFinishLaunching? - ios

Here is my situation:
I have a TaskListViewController(UITableView) which need Internet.
So I used AFNetworking to do some login work(with demo account and password) before the TaskList is showed.
Now I need to let the user set their account and password.
So I just want to present a Modal View(ZTCUserSettingsViewController) before login.
And I cost a lot time, it still doesn't work.
Is there any way to solve this?
Thank you.
ZTCAPIClient : AFHTTPClient
ZTCTaskListViewController : UITableViewController
code
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[[AFNetworkActivityIndicatorManager sharedManager] setEnabled:YES];
[ZTCAPIClient login];
UITableViewController *viewController = [[ZTCTaskListViewController alloc] initWithStyle:UITableViewStylePlain];
UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:viewController];
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
self.window.backgroundColor = [UIColor whiteColor];
self.window.rootViewController = nav;
[self.window makeKeyAndVisible];
return YES;
}

I don't see where your sample code attempts to present the modal view controller. Perhaps it happens inside of -[ZTCAPIClient login].
Generally speaking, you can't present a modal view controller until after the presenting view controller's view is in the view hierarchy. For your specific question, this means your program needs to present the modal sometime after the line that reads [self.window makeKeyAndVisible];. It will not work if your program tries to present the modal view controller before, in fact, you'll probably see an error message logged to the debug console.
On a side note, you should be careful not to make your program do too much in -application:didFinishLaunchingWithOptions:. If your program takes too long, the system may kill your app the iOS App Programming Guide states:
Your application:willFinishLaunchingWithOptions: and
application:didFinishLaunchingWithOptions: methods should always be as
lightweight as possible to reduce your app’s launch time. Apps are
expected to launch and initialize themselves and start handling events
in less than 5 seconds. If an app does not finish its launch cycle in
a timely manner, the system kills it for being unresponsive. Thus, any
tasks that might slow down your launch (such as accessing the network)
should be executed asynchronously on a secondary thread.
When launching into the foreground, the system also calls the
applicationDidBecomeActive: method to finish the transition to the
foreground. Because this method is called both at launch time and when
transitioning from the background, use it to perform any tasks that
are common to the two transitions.

Here is my final solution:
launch up:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[[AFNetworkActivityIndicatorManager sharedManager] setEnabled:YES];
UINavigationController *nav = [[UINavigationController alloc] init];
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
self.window.backgroundColor = [UIColor whiteColor];
self.window.rootViewController = nav;
[self.window makeKeyAndVisible];
//important!
[ZTCAPIClient registerUserInfo];
return YES;
}
in ZTCAPIClient.m:
+ (void) registerUserInfo {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *account = [defaults stringForKey:#"account"];
if(!account) {
// load default value
[self performSelector:#selector(registerDefaultsFromSettingsBundle)];
ZTCUserSettingsViewController *userSettingsView = [[ZTCUserSettingsViewController alloc] init];
UINavigationController *usersSettingsNav = [[UINavigationController alloc] initWithRootViewController:userSettingsView];
[[[[[UIApplication sharedApplication] delegate] window] rootViewController] presentModalViewController:usersSettingsNav animated:NO];
} else {
DLog(#"**********************");
if ([ZTCAPIClient loginWithAccount:[defaults stringForKey:#"account"] Password:[defaults stringForKey:#"password"] Mode:[defaults stringForKey:#"requestType"] BaseURL:[defaults stringForKey:#"url"]]) {
DLog(#"Log in SUCCESS");
UITableViewController *viewController = [[ZTCTaskListViewController alloc] initWithStyle:UITableViewStylePlain];
UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:viewController];
[[[[UIApplication sharedApplication] delegate] window] setRootViewController:nav];
} else {
DLog(#"Log in FAIL");
ZTCUserSettingsViewController *userSettingsView = [[ZTCUserSettingsViewController alloc] init];
UINavigationController *usersSettingsNav = [[UINavigationController alloc] initWithRootViewController:userSettingsView];
[[[[[UIApplication sharedApplication] delegate] window] rootViewController] presentModalViewController:usersSettingsNav animated:NO];
}
}
}

Related

NavigationController presenting ViewController twice

I installed third party library using pods, which has it's own navigationController. I my existing project I am setting the rootViewController in the app delegate. I am pushViewController to the library ViewController (which is in it's own navigation stack). When I exit and then push to the library for a second time the ViewAlready seems to be there before transitioning the same view over the top.
In my appDelegate...
-(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
appbarViewController = [[AppBarViewController alloc] init];
[self loadNormalFlow];
[self.window makeKeyAndVisible];
}
- (void)loadNormalFlow
{
self.navigation = [[UINavigationController alloc]initWithRootViewController:appbarViewController];
self.window.rootViewController = self.navigation;
[self.navigation setNavigationBarHidden:YES];
}
-(void)displayLibraryView
{
ACSViewController *acs = [[ACSViewController alloc] init];
[self.navigation pushViewController:acs animated:nil];
}
Within library ViewController class to return back to original rootViewController i am using
[[UIApplication sharedApplication].delegate performSelector:#selector(loadNormalFlow)];
I seems like it is not properly dismissing the library ViewController, or it does a push twice? Any idea?

Can [self.window makeKeyAndVisible]; be called before setting rootviewcontroller

My requirement is that UITabBarController is the rootviewcontroller and on very first time of app launch I want to show login procedure which is inside UINavCon, and I am displaying it through presentViewController.
I dont want the UITabBarController visible for first time and dont want to how login UINavCon popping as modal.
I want to make user experience that if app starts for first time login UINavCon should be visible. So here is my code:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
[self.window makeKeyAndVisible];//is it correct to call it here?
LoginVC *loginObj = [[LoginVC alloc]init];
self.navigationController = [[UINavigationController alloc] initWithRootViewController:cellPhoneNumber];
self.tabBarController = [[UITabBarController alloc]init];
self.window.rootViewController = self.tabBarController;
[self.tabBarController presentViewController:self.navigationController animated:NO completion:^{}];
return YES;
}
I am calling [self.window makeKeyAndVisible]; on second line right after uiwindow alloc init. Is it correct do this or I can experience problems like viewcontroller not receiving events or orientations notifications?
you can call it whenever you want. Calling it affects the window's z-index and screen property.
it doesnt depend on any specific content being set.
You haven't mentioned that whether you got the code working or not by using your implementation. Anyways I have done similar kind of implementation recently where we need to present login controller and then tabBarController after logging in, so just sharing my implementation.
Create your login controller and present it in didFinishLaunching method.
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
LoginController *loginCObj= [[[MainScreenController alloc]init]autorelease];
UINavigationController *navigationControllerObj = [[[UINavigationController alloc]initWithRootViewController:loginObj]autorelease];
self.window.rootViewController = navigationControllerObj;
[self.window makeKeyAndVisible];
After that on succesful login in your login view controller, call an appDelegate public method
In login controller
AppDelegate *appDel = (AppDelegate *)[[UIApplication sharedApplication] delegate];
[appDel applicationLoggedInSuccesfully];
In your appDelegate file, add a method like this:
-(void)applicationLoggedInSuccesfully{
UINavigationController *nv1 = [[[UINavigationController alloc] initWithNibName:nil bundle:nil]autorelease];
TabController1 *v1 = [[[TabController1 alloc] initWithNibName:nil bundle:nil]autorelease];
[nv1 pushViewController:v1 animated:NO];
UITabBarController *tabController = [[[UITabBarController alloc] init]autorelease];
tabController.viewControllers = #[nv1];
tabController.delegate = self;
self.window.rootViewController = tabController;
[self.window makeKeyAndVisible];
}
Hope it will help you.

iOS Login Modal

I'm writing an iOS application which I need to have the user login to. The login will need to make a JSON request to a web service, then store the user details in Core Data or wherever is best.
Further, I need to have the login modal appear before the main application kicks in, I know that for this I call it in - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions.
So, I need a simple login, username and password field, make a request and then store the resulting JSON data into somewhere, core data perhaps?
I've searched high and low for a login example, but they're all very rudimentary or not doing quite what I want. I'm thinking of creating a xib file then calling that, but I'm unsure about it all.
I was wondering the same question several days ago.
Here is my solution:
in didFinishLaunchingWithOptions ,I invoke a method check whether there is account and password in your database(core data? i just use the userdefault).
If there is , try login, if login fail, present a modal view. if successed, set your appdelegate.window.rootviewcontroller the main viewcontroller
If there is nothing, show modal view.
or login failed, blablabla...
sorry for my poor English.
here is my code:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[[AFNetworkActivityIndicatorManager sharedManager] setEnabled:YES];
UINavigationController *nav = [[UINavigationController alloc] init];
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
self.window.backgroundColor = [UIColor whiteColor];
self.window.rootViewController = nav;
[self.window makeKeyAndVisible];
[ZTCAPIClient registerUserInfo];
return YES;
}
+ (void) registerUserInfo {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *account = [defaults stringForKey:#"account"];
if(!account) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// load default value
[self performSelector:#selector(registerDefaultsFromSettingsBundle)];
dispatch_async(dispatch_get_main_queue(), ^{
ZTCUserSettingsViewController *userSettingsView = [[ZTCUserSettingsViewController alloc] init];
UINavigationController *usersSettingsNav = [[UINavigationController alloc] initWithRootViewController:userSettingsView];
[[[[[UIApplication sharedApplication] delegate] window] rootViewController] presentModalViewController:usersSettingsNav animated:NO];
[ZTCNotice showSuccessNoticeInView:userSettingsView.view title:[NSString stringWithFormat:#"%#,%#",NSLocalizedString(#"login first time use title", nil),NSLocalizedString(#"login first time use message", nil)]];//TODO
});
});
} else {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
if ([ZTCAPIClient loginWithAccount:[defaults stringForKey:#"account"] Password:[defaults stringForKey:#"password"] BaseURL:[defaults stringForKey:#"url"]]) {
//DLog(#"Log in SUCCESS");
dispatch_async(dispatch_get_main_queue(), ^{
UITableViewController *viewController = [[ZTCTaskListViewController alloc] initWithStyle:UITableViewStylePlain];
UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:viewController];
[[[[UIApplication sharedApplication] delegate] window] setRootViewController:nav];
});
} else {
//DLog(#"Log in FAIL");
dispatch_async(dispatch_get_main_queue(), ^{
ZTCUserSettingsViewController *userSettingsView = [[ZTCUserSettingsViewController alloc] init];
UINavigationController *usersSettingsNav = [[UINavigationController alloc] initWithRootViewController:userSettingsView];
[[[[[UIApplication sharedApplication] delegate] window] rootViewController] presentModalViewController:usersSettingsNav animated:NO];
[ZTCNotice showErrorNoticeInView:userSettingsView.view title:NSLocalizedString(#"login fail title", nil) message:NSLocalizedString(#"login fail message", nil)];
});
}
});
}
}
Just make didFinishLaunchingWithOptions modally push your LoginViewController to the navigation stack. If you're using a storyboard, you can just make your LoginViewController the root view controller, or alternatively place a segue, give it an identifier, and just call [rootViewController performSegueWithIdentifier:#"YourSegueId" sender:self].

Pushing ViewController gives NSInvalidArgumentException

I'm a newbie iOS developer, recently developed several Android apps, but I'm not familiar with iOS jargon. Let me explain my problem.
I want to use two different UIViewController. I've created .h and .m files for both controller. My plan is to push the secont view controller on top of the first view controller five seconds after the first view controller appears on the screen. I mean the first view controller is something like splash screen or similar.
Here is my contribution. In the first view controller, I defined (one of them implemented of course) these two method:
-(void) pushSecondController {
SecondViewController *secondController = [[SecondViewController alloc]
initWithNibName: nil
bundle: NULL];
[self.navigationController pushViewController: secondController animated: YES];
}
-(void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[self performSelector: #selector(pushViewController:animated:)
withObject: nil
afterDelay: 5.0f];
}
And the second view controller looks like this:
- (void)viewDidLoad
{
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
self.title = #"Second Controller";
}
I've changed only viewDidLoad method. When I ran the simulator, first view controller worked well and waiting 5 seconds and crashed. Output looks like:
2012-08-24 10:46:34.104 NavApplication[20355:f803] -[ViewController pushViewController:]: unrecognized selector sent to instance 0x6e7f780
2012-08-24 10:46:34.107 NavApplication[20355:f803] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[ViewController pushViewController:]: unrecognized selector sent to instance 0x6e7f780'
Let me ask one more question: I know there are differences between methodName and methodName:. Can anyone explain what's difference?
Any help would be appreciated.
UPDATE:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
self.viewController = [[ViewController alloc] initWithNibName:#"ViewController" bundle:nil];
self.window.rootViewController = self.viewController;
self.navigationController = [[UINavigationController alloc] initWithRootViewController:self.viewController];
[self.window makeKeyAndVisible];
[self.window addSubview: self.navigationController.view];
return YES;
}
Change #selector(pushViewController:animated:) to #selector(pushSecondController).
As mentioned above, you probably want to change your performSelector command to:
[self performSelector: #selector(pushSecondController)
withObject: nil
afterDelay: 5.0f];
because you want it to call your pushSecondController method, and not pushViewController:animated:.
Regarding your second question: the difference between methodName and methodName: is that the : at the end of methodName: signifies that this method takes a parameter. So, you could have the following methods:
- (void)listItems
{
// ...
}
- (void)insertItem:(NSDictionary *)item
{
// ...
}
When passing a reference to them into #selector, for the first method you'd just do #selector(listItems), because it takes no parameters, and for the latter you'd do #selector(insertItem:) because it takes a parameter.
UPDATE
Just saw your applicationDidLaunch code. You probably want to rearrange things so that you add your ViewController to your UINavigationController, and then set the UINavigationController as the rootViewController of your window. Like so:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
self.viewController = [[ViewController alloc] initWithNibName:#"ViewController" bundle:nil];
self.navigationController = [[UINavigationController alloc] initWithRootViewController:self.viewController];
self.window.rootViewController = self.navigationController;
[self.window makeKeyAndVisible];
return YES;
}
please change your didFinishLaunchingWithOptions method to:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
self.viewController = [[ViewController alloc] initWithNibName:#"ViewController" bundle:nil];
self.navigationController = [[UINavigationController alloc] initWithRootViewController:self.viewController];
self.window.rootViewController = self.navigationController;
[self.window makeKeyAndVisible];
return YES;
}

Basic table program in iOS - How can I tell my table to reload data from the application delegate?

I've got your standard table application built right now. Window has a UINavigationController as its rootViewController, and that UINavigationController was initialized with its rootViewController as my custom UITableController.
My applications table data changes over time. If I open my application up from a suspended state then my data is stale. How do I get applicationWillEnterForeground to update my data? I tried something awkward like
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
self.window.backgroundColor = [UIColor whiteColor];
HabitsTable* vc = [[HabitsTable alloc] init];
[vc initCoreData];
UINavigationController* nav = [[UINavigationController alloc] initWithRootViewController:vc];
self.window.rootViewController = nav;
[self.window makeKeyAndVisible];
return YES;
}
- (void)applicationWillEnterForeground:(UIApplication *)application
{
[[[[self window] rootViewController] navigationController] rootViewController]
}
But, no surprise, that doesn't work. I'm not sure how to get at my UITableController?
I'd recommend using the NSNotificationCenter. You can see a nice example of how to use the class here.
I believe your close try:
UINavigationController *navController = (UINavigationController*)[[self window] rootViewController];
HabitsTable *tableView = (HabitsTable*)[navController topViewController];

Resources