Create a ViewController that shows on First Launch Only - ios

SO I am creating an iOS app that involves allowing the user to be able to receive push notifications. I already have the following code in my appDelegate didFinishLaunchingWithOptions.
if ([application respondsToSelector:#selector(isRegisteredForRemoteNotifications)])
{
// iOS 8 Notifications
[application registerUserNotificationSettings: [UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeSound | UIUserNotificationTypeAlert | UIUserNotificationTypeBadge) categories:nil]];
[application registerForRemoteNotifications];
}
So far so good, however, i would like to create an initial view controller that only shows on first launch, it will hopefully explain and justify to the user why it need the ability to send the user push notifications, they can then press continue and it will ask them by executing the code above. This would hopefully only happen on the first launch. Any suggestions?
Thanks

I think you can do something like this
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.window.backgroundColor = [UIColor whiteColor];
BOOL userHasOnboarded = [[NSUserDefaults standardUserDefaults] boolForKey:#"isFirstTime"];
if (userHasOnboarded) {
[self setupNormalRootViewControllerAnimated:YES];
}
else {
self.window.rootViewController = [self PushExplanation];
}
[self.window makeKeyAndVisible];
return YES;
}
- (void)setupNormalRootViewControllerAnimated:(BOOL)animated {
UIViewController *mainVC = [UIViewController new];
mainVC.title = #"Home View Controller";
if (animated) {
[UIView transitionWithView:self.window duration:0.5 options:UIViewAnimationOptionTransitionCrossDissolve animations:^{
self.window.rootViewController = [[UINavigationController alloc] initWithRootViewController:mainVC];
} completion:nil];
}
else {
self.window.rootViewController = [[UINavigationController alloc] initWithRootViewController:mainVC];
}
}
In your PushExplanation method you can show the UIviewController you want to show once and then in the same UIViewController you can ask for Push Notifications like this
[[UIApplication sharedApplication] registerForRemoteNotificationTypes:
(UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound | UIRemoteNotificationTypeAlert)];
Hope this help you

This is trivial.
Add a boolean flag haveDisplayedFirstVC to NSUserDefaults. Read it at startup. If it's false, you need to display your first time VC.
In that case, if you're using storyboards, use instantiateViewControllerWithIdentifier: to load a view controller from your storyboard. If you're using nibs, use initWithNibName:bundle:
Once you've created and configured the view controller, display it on top of your standard interface's VC using `presentViewController:animated:completion:' but with animated = NO. That way it will appear on top of your normal interface as the app starts up. (Or animate it onto the screen, whichever you prefer.)

Add the status (Bool) in NSUserDefault and Firsttime check if the key is there or not .
If there is no value,then display your initial viewcontroller and change the status to yes(probably).
Next time normal page will load.

Related

RootViewController doesn't respond when set in didFinishLaunchingWithOptions

I am developing an iOS application which needs to be launched with different rootViewControllers (for example if the user had already registered, or if it started after a local notification), and these viewControllers are in different storyboards. I thought one way to do this, is to leave the Main interface field blank in the Deployment info, and configure the rootViewController manually.
I set the rootViewController in my application delegate class:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
IDOptionsViewController *mainViewController = [storyboard instantiateViewControllerWithIdentifier:#"options"];
self.window.rootViewController = mainViewController;
[self.window makeKeyAndVisible];
return YES;
}
When I launch the application, the rootViewController appears, but it does not respond to any user interaction (I tested the viewController, it works fine other places).
I'm new to Objective-C and iOS, so I'm not sure I even understand storyboards correctly.
I'm using iOS SDK 7.1, Xcode 5.
Thanks for the answers.
I had the same problem. In may case the root UIViewController called
[[UIApplication sharedApplication] beginIgnoringInteractionEvents]
So, make sure user interaction events are enabled by calling [UIApplication sharedApplication].isIgnoringInteractionEvents]. To enable user interaction events call [[UIApplication sharedApplication] endIgnoringInteractionEvents].

Open view controller when APNS is received in iOS

Hey I'm new to iPhone and I have been trying to use an Apple push notification. Basically, what I want to do is that when user clicks on the received push notification message, then i need to open a specific view controller. I have added custom data with key parameter "type" to my payload JSON, so on behalf of notification type value i need to open particular view controller instead of the main view controller.
here is my payload JSON :
{"aps":{"alert":"This is testing message","type":"Notify","badge":1,"sound":"default"}}
my code is :
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[[UIApplication sharedApplication] registerForRemoteNotificationTypes:
(UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound | UIRemoteNotificationTypeAlert)];
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
splashViewController = [[SplashViewController alloc] initWithNibName:#"SplashViewController" bundle:nil];
self.window.rootViewController = splashViewController;
[self.window makeKeyAndVisible];
return YES;
}
- (void)application:(UIApplication*)application didReceiveRemoteNotification:(NSDictionary*)userInfo
{
//this will call when your app will get any notification from server.
[[UIApplication sharedApplication] setApplicationIconBadgeNumber:0];
[[UIApplication sharedApplication] cancelAllLocalNotifications];
NSDictionary *aps = (NSDictionary *)[userInfo objectForKey:#"aps"];
NSMutableString *notificationType = [aps objectForKey:#"type"];
NSLog(#"notification type is = %#", notificationType);
//For redirect to the view
UIViewController *viewController;
if([notificationType isEqualToString:#"Notify"]){
//Notify updates
UpdatesViewController *uvc1 = [[UpdatesViewController alloc] initWithNibName:#"UpdatesViewController" bundle:nil];
viewController = uvc1;
}
else {
//Voting & QA
VotingViewController *votingViewController = [[VotingViewController alloc] initWithNibName:#"VotingViewController" bundle:nil];
viewController = votingViewController;
}
[self.window.rootViewController presentViewController:viewController animated:YES completion:NULL];
}
My code is not presenting other view controller. As mentioned in my code, i am getting two kind of web service (notification type) 1. Notify and 2. Voting from Apple. i dont know how to present specific view controller on behalf of notification type, if type is "notify" then i need open UpdatesViewController else it will open other viewcontroller. If anyone knows how to do this, please help me.
Thanks.
//For redirect to the view
UIViewController *uvc;
if([notificationType isEqualToString:#"Notify"]){
UIViewController *updates = [[UpdatesViewController alloc] initWithNibName:#"UpdatesViewController" bundle:nil];
// add updates specific data updates.data = [aps objectForKey:#"data"];
uvc = updates;
}
else if([notificationType isEqualToString:#"Voting "]) {
UIViewController *voting = [[VotingViewController alloc] initWithNibName:#"VotingViewController" bundle:nil];
// add voting specific data voting.data = [aps objectForKey:#"data"];
uvc = voting;
}
[self.window.rootViewController presentViewController:uvc animated:YES completion:NULL];
you should check the aps in didFinishLaunchingWithOptions.
didReceiveRemoteNotification only works when app is running or suspended.

Issue in open view controller when APNS is received in iOS

Hey I'm new to iPhone and I have been trying to use an Apple push notification. Basically, what I want to do is that when user clicks on the received push notification message, then i need to open a specific view controller, it is working for me. I have added custom data with key parameter "type" to my payload JSON.
here is my payload JSON :
{"aps":{"alert":"This is testing message","type":"Notify","badge":1,"sound":"default"}}
here is my code :
#synthesize viewControllerNotify;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[[UIApplication sharedApplication] setApplicationIconBadgeNumber:0];
[[UIApplication sharedApplication] cancelAllLocalNotifications];
[[UIApplication sharedApplication] registerForRemoteNotificationTypes:
(UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound | UIRemoteNotificationTypeAlert)];
screenBounds = [[UIScreen mainScreen] bounds];
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
if (launchOptions != nil) {
//Launched from push notification
NSDictionary *userInfo = [launchOptions valueForKey:#"UIApplicationLaunchOptionsRemoteNotificationKey"];
NSDictionary *apsInfo = [userInfo objectForKey:#"aps"];
NSMutableString *notificationType = [apsInfo objectForKey:#"type"];
//For redirect to the view
if([notificationType isEqualToString:#"Notify"]){
//Notify updates
UpdatesViewController *uvc1 = [[UpdatesViewController alloc] initWithNibName:#"UpdatesViewController" bundle:nil];
self.viewControllerNotify = uvc1;
}
else if([notificationType isEqualToString:#"Voting"] || [notificationType isEqualToString:#"QA"]){
//Voting & QA
VotingQAViewController *votingQAViewController = [[VotingQAViewController alloc] initWithNibName:#"VotingQAViewController" bundle:nil];
self.viewControllerNotify = votingQAViewController;
}
else if([notificationType isEqualToString:#"Survey"] || [notificationType isEqualToString:#"Quiz"]){
//Survey & Quizzes
SurveysViewController *surveysViewController = [[SurveysViewController alloc] initWithNibName:#"SurveysViewController" bundle:nil];
self.viewControllerNotify = surveysViewController;
}
UINavigationController *nav=[[UINavigationController alloc]initWithRootViewController:self.viewControllerNotify];
self.window.rootViewController = nav;
[nav setNavigationBarHidden:YES];
}
else{
//Normal Launch
if(screenBounds.size.height == 568) {//iPhone5
splashViewController = [[SplashViewController alloc] initWithNibName:#"SplashViewController_5" bundle:nil];
}
else{
splashViewController = [[SplashViewController alloc] initWithNibName:#"SplashViewController" bundle:nil];
}
UINavigationController *nav=[[UINavigationController alloc]initWithRootViewController:splashViewController];
self.window.rootViewController = nav;
[nav setNavigationBarHidden:YES];
}
[self.window makeKeyAndVisible];
return YES;
}
- (void)application:(UIApplication*)application didReceiveRemoteNotification:(NSDictionary*)userInfo
{
NSDictionary *aps = (NSDictionary *)[userInfo objectForKey:#"aps"];
int badge = [[aps objectForKey:#"badge"] intValue];
NSMutableString *notificationType = [aps objectForKey:#"type"];
NSLog(#"Number of badge is = %d", badge);
NSLog(#"notification type is = %#", notificationType);
//For redirect to the view
if([notificationType isEqualToString:#"Notify"]){
//Notify updates
UpdatesViewController *uvc1 = [[UpdatesViewController alloc] initWithNibName:#"UpdatesViewController" bundle:nil];
self.viewControllerNotify = uvc1;
}
else if([notificationType isEqualToString:#"Voting"] || [notificationType isEqualToString:#"QA"]){
//Voting & QA
VotingQAViewController *votingQAViewController = [[VotingQAViewController alloc] initWithNibName:#"VotingQAViewController" bundle:nil];
self.viewControllerNotify = votingQAViewController;
}
else if([notificationType isEqualToString:#"Survey"] || [notificationType isEqualToString:#"Quiz"]){
//Survey & Quizzes
SurveysViewController *surveysViewController = [[SurveysViewController alloc] initWithNibName:#"SurveysViewController" bundle:nil];
self.viewControllerNotify = surveysViewController;
}
[[UIApplication sharedApplication] setApplicationIconBadgeNumber:0];
[[UIApplication sharedApplication] cancelAllLocalNotifications];
UINavigationController *nav=[[UINavigationController alloc]initWithRootViewController:self.viewControllerNotify];
self.window.rootViewController = nav;
[nav setNavigationBarHidden:YES];
[self.window makeKeyAndVisible];
}
My question is: Is this the correct way to redirect to the view controller when push notification is received?
Using the above code, it is redirecting to the view controller class on behalf on Notification type (custom key of notification payload json) successfully in forground and background mode but that redirected View Controller back button is not working. I don't know where i am doing wrong here. If anyone knows then please help me. Thanks.
Better You can post the notification using NSNotificationCentre in didReceiveRemoteNotification and receive the notification on the other classes. So that you can push the required view controller from the current view controller (need not to set as root view controller). Then the back button will works.
For Ex.
[[NSNotificationCenter defaultCenter] postNotificationName:kMesssagePushNotification object:nil userInfo:userInfo];
The back button is not working, because you are setting the target view as a root view. You should instead construct the full navigation stack, i.e. usually, if you have a navigation view controller as a root view controller, you should create that first and then construct all the view controllers that are between the root and your leaf view.
For example, if MainViewController would be your normal start view and the back button from UpdatesViewController should lead to MainViewController you would do:
MainViewController *mainVC = [[MainViewController alloc] initWithNibName:#"MainViewController" bundle:nil];
UpdatesViewController *updatesVC = [[UpdatesViewController alloc] initWithNibName:#"UpdatesViewController" bundle:nil];
UINavigationController *navVC=[[UINavigationController alloc]initWithRootViewController:mainVC];
[navVC setViewControllers:#[mainVC, updatesVC] animated:NO];
self.window.rootViewController = navVC;
There are two chances that you will access the notification data.
If you receive the notification when your app isn't on, then click the notification and you will get the notification data in the following function:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
use
launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey]
to access the notification data and open the view controller you expected.
If you receive the notification when your app is on, but your app can be in background or foreground. If it is the former case, you will receive the notification in notification center, your app will invoke the following function after you click the notification:
-(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
If it is the later case, you app will directly invoke the function before. And you can distinguish them using the flowing code:
UIApplicationState state = [[UIApplication sharedApplication] applicationState];
if (state == UIApplicationStateBackground || state == UIApplicationStateInactive){
//notification is received when your app is in background
//open the view controller you expected
}else if(state == UIApplicationStateActive){
//notification is received when your app is in foreground
//do nothing
}
sorry for my pool English, hope it helps~

How to presentModalViewController in applicationDidFinishLaunching?

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];
}
}
}

How to open a ViewController before application starts in iOS

I am working in iOS 5,and before loading my application,I want to open a another view controller,where the user should enter some data,for eg.password and when the password matches ,application will be opened,I am not getting how to do this..I tried some code ,which I have written below
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
if(somecondition)
{
ViewController *View =[[ViewController alloc]initWithNibName:#"ViewController" bundle:nil];
[_window addSubview:View.view];
}
return YES;
}
But I dont know whether it is a right way,so friends,please help me out..
Regards
Ranjit
You should use
[self.window setRootViewController:yourViewController]
instead of addSubview to your window.
BTW, searching before asking is a good habit. ;)
If you want to show a view like the loginView or loadingView, you can set it as your rootViewController, when did loaded, you can reset your rootViewController.
Note, in your ProjectAppDelegate.m, you can get window
by self.window, and in other child view controller's, you'll need
[[[UIApplication sharedApplication] delegate] window]
to get your main window.
Another simple way to meet your requirement is that you can just present a modalView before showing your app. Dismiss it after done and then start your app.
You can get more suggestion HERE.
BTW, I'm sorry I didn't get your comments' notification when you are write at other users comment area a few days ago. :( You should add # before the user's name when you comment at somewhere else.
You can create some bool variable for checking is this a first start or another. The best place to store this bool is NSUserDefaults. Well, if this is a first start then show your LoginViewController, if not - execute regular code:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
UIViewController *startVC = nil;
if (isFirstLaunch){
startVC = [[[LoginViewController alloc] initWithNibName:#"LoginView" bundle:nil] autorelease];
}
else{
startVC = [[[WorkspaceViewController alloc] initWithNibName:#"WorkspaceView" bundle:nil] autorelease];
}
navController = [[UINavigationController alloc] initWithRootViewController:startVC];
[self.window makeKeyAndVisible];
[self.window addSubview:navController.view];
return YES;
}

Resources