Proper view controller not displayed when tapping push notification - objective-c

I have implemented push notifications and am attempting to take the user to another view controller (opposed to just opening the app) when they tap the notification. I was going off of the apple website example but have not had any luck yet.
It seems that it never even enters my code as it should. I tried putting an NSLog to prove that and I was right.
Does anyone know why? The view controller I want implemented is called statsViewController.
Thanks
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
NSDictionary *remoteNotif = [launchOptions objectForKey: UIApplicationLaunchOptionsRemoteNotificationKey];
if (remoteNotif) {
NSLog (#"We are here now");
[self handleRemoteNotification:application userInfo:remoteNotif]; //custom method where View controller will be implemented
return YES;
}
[window addSubview:tabBarController.view];
[window makeKeyAndVisible];
return YES;
}
-(void) handleRemoteNotification:(UIApplication *)application userInfo:(NSDictionary *)userInfo {
application.applicationIconBadgeNumber = 0;
statsViewController* viewController = [[statsViewController alloc] init];
[navController pushViewController:viewController animated:YES]; //Don't think this is right since I never use it in didFinishLaunching... I simply just declared it delegate.h
[viewController release];
}

Related

How do you access a uitableview inside a uinavigationcontroller inside a uitabbarcontroller from app delegate?

Im trying to access a refresh control method in a uitableview which is inside a navigationcontroller from a tabbarcontroller which is my root, but im having trouble getting an exact handle.
This is my code so far in the AppDelegate but it doesn't work...
UITableViewController *tableView = (UITableViewController *)[[self.tabbarController viewControllers][0] tableView];
[tableView.refreshControl beginRefreshing];
I have 5 tab bar items which I believe I can access via [0],[1],[2],[3]
And my code in the UITableView (though probably doesnt matter)...
// Add Refresh Control
UIRefreshControl *refreshControl = [[UIRefreshControl alloc] init];
[refreshControl addTarget:appDelegate action:#selector(forceDownload) forControlEvents:UIControlEventValueChanged];
self.refreshControl = refreshControl;
[refreshControl release];
Any help would be greatly appreciated as i cant find any online that access as deep as this.
If you need communication between objects that are unrelated, i think that the best option is to use NSNotifications. This allows you to use the singleton object[NSNotificationCenter defaultCenter], to pass notifications from one object to another (or many others).
So you can put the AppDelegate object (or other object) to observe a specific notification and then post the notification with the tableviewController when you need to refresh the control.
In app delegate class you can add the observer like:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(forceDownload)
name:#"ForceDownloadNotification"
object:nil];
And, in the tableviewController you can post de notification like:
[[NSNotificationCenter defaultCenter] postNotificationName:#"ForceDownloadNotification"
object:self];
Here, I used the name "ForceDownloadNotification" as the name for the notification. You can use the name that you want, but in order this solution work properly, you must use the same name when you start the observation and when you post the notification.
Here you have a tutorial about this subject.
I like Luis Espinoza's approach but that doesn't answer the question per se.
If you want to call a method inside your UITableViewController nested inside a UINavigationController which is the rootViewController for your App Delegate. First we create a navigationController with the UITableViewController (or a subclass):
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
CustomTableViewController *nuTableVC = [[CustomTableViewController alloc] initWithStyle:UITableViewStylePlain];
UINavigationController *nuNavController = [[UINavigationController alloc] initWithRootViewController:nuTableVC];
self.window.rootViewController = nuNavController;
[self.window makeKeyAndVisible];
return YES;
}
Then in your UITableViewController (or subclass) you setup the refreshcontrol just like you asked:
- (void)viewDidLoad {
[super viewDidLoad];
// Add Refresh Control
UIRefreshControl *refreshControl = [[UIRefreshControl alloc] init];
[refreshControl addTarget:[[UIApplication sharedApplication] delegate]
action:#selector(forceDownload)
forControlEvents:UIControlEventValueChanged];
self.refreshControl = refreshControl;
}
Finally to access the UItableViewController you must check if the instances are really the classes that you want, here is my implementation for the method you created (forceDownload) in your App Delegate:
- (void)forceDownload {
NSLog(#"force download method in App Delegate");
UINavigationController *someNavController = (UINavigationController*)[_window rootViewController];
UIViewController *vcInNavController = [[someNavController viewControllers] objectAtIndex:0];
if ([vcInNavController isKindOfClass:[CustomTableViewController class]]) {
NSLog(#"it is my custom Table VC");
NSLog(#"here we can stop the refresh control, or whatever we want");
CustomTableViewController *customTableVC = (CustomTableViewController *)vcInNavController;
[customTableVC.refreshControl performSelector:#selector(endRefreshing)
withObject:nil
afterDelay:1.0f];
}
}
I personally prefer using NSNotificationCenter because is simpler, but that doesn't mean we can't access the objects the way you originally planed.
(if you want the example code just ask for it).
Regards.
If your goal is truely to just keep your refresh control synch'd with the networkActivityIndicator then one option is KVO.
In the viewController's viewDidAppear: add something like this
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
UIApplication *application = [UIApplication sharedApplication];
[application addObserver:self
forKeyPath:#"networkActivityIndicatorVisible"
options:NSKeyValueObservingOptionNew
context:myContext];
self.refreshControl.refreshing = [application isNetworkActivityIndicatorVisible];
}
Then make sure to remove this observer when the viewController doesn't need it - perhaps in viewDidDisappear:
- (void)viewWillDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
[[UIApplication sharedApplication] removeObserver:self
forKeyPath:#"networkActivityIndicatorVisible"
context:myContext];
}
Now for the actual work
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context;
{
if (myContext == context) {
self.refreshControl.refreshing = [change[NSKeyValueChangeNewKey] boolValue];
} else {
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}

self.navigationController == nil after awaking from LocalNotification

I'm working in Xcode 4.3.2
I implemented Local Notifications that alert the user of a new event at a predetermined time. So when my app is in the background and the clock strikes 8 am (for instance), the user will get a notification from my app.
When the user decides to view the app from the background I load a nib. Currently, this nib works properly: it shows the view as it was it arranged in the nib. However, after the nib is shown to the user, I want to forward the user to a different view in the LocalNotificationsHandler.m. When I attempt to push the second view, my app fails. So while there isn't an error message, it seems the second nib will not load.
In short the flow goes as follows:
user gets notification while my app is running in the background
user chooses to view the app
the LocalNotificationsHandler nib will load
self.navigationController == nil (in LocalNotificationsHandler.m)
self.navigationController will not "[pushViewController: "new view" animated:YES]" to get a new view
I'm wondering if there is something I'm missing from my AppDelegate.m file so I've included
"didFinishLaunchingWithOptions" from my AppDelegate.m file:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
// Add the navigation controller's view to the window and display.
NSLog(#"did finish launching with options");
[self.window addSubview:tabBarController.view];
[self.window makeKeyAndVisible];
[[UIApplication sharedApplication] registerForRemoteNotificationTypes:(UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound)];
if (self.locationManager == nil)
{
locationManager = [[CLLocationManager alloc] init];
locationManager.purpose = #"We will try to use you location";
}
if([CLLocationManager locationServicesEnabled])
{
[self.locationManager startUpdatingLocation];
}
self.navigationController.navigationBar.tintColor = nil;
return YES;
}
You are using the outdated (since iOS 3) method of adding the viewcontroller's view to the main UIWindow. That should be looking like this:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// create properly sized window
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// create instance of root VC and assign to window
MainViewController *vc = [[MainViewController alloc] init];
self.window.rootViewController = vc;
[vc release];
[self.window makeKeyAndVisible];
return YES;
}
The navigationController property of a view controller is ONLY set if it is actually presented from a UINavigationController.
See this writeup for more information: http://www.cocoanetics.com/2012/11/revisited/

Correct pattern for refreshing data in UIViewController's

I am trying to refresh my view data when the application becomes the active app again. I believe the correct pattern is to have the app delegate tell it's view to reload it's data when applicationDidBecomeActive is called.
However, I am having trouble finding the UIViewController from within the Delegate:
- (void)applicationDidBecomeActive:(UIApplication *)application {
MyFancyViewController* controller = //how do I get my view controller???
[controller refreshData];
}
Also, can I count on the view controller still being allocated, or is there a chance it would go away? I'm using iOS 5 Storyboard's if that makes any difference.
Update:
I think I got it:
- (void)applicationDidBecomeActive:(UIApplication *)application
{
UIViewController* root = _window.rootViewController;
UINavigationController* navController = (UINavigationController*)root;
OctainViewController* mycontroller = (OctainViewController*)[[navController viewControllers] objectAtIndex:0];
[mycontroller refresh:nil];
}
Yeah, this does the trick:
- (void)applicationDidBecomeActive:(UIApplication *)application
{
UIViewController* root = _window.rootViewController;
UINavigationController* navController = (UINavigationController*)root;
OctainViewController* mycontroller = (OctainViewController*)[[navController viewControllers] objectAtIndex:0];
[mycontroller refresh:nil];
}
Why not refresh your data in the respective view controller viewWillAppear method?

UINavigationController, simply hiding the navigation bar

I've I have a simple noob question, I would like to hide the navigation bar of a UINavigationController, but I'm pretty sure I`m not calling the right object .
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
UIViewController *rootViewController = [[[TestViewController alloc] init] autorelease];
viewController = [ [UINavigationController alloc] initWithRootViewController:rootViewController];
[window addSubview:viewController.view];
[window addSubview: rootViewController.view];
[window makeKeyAndVisible];
return YES;
}
Now, I`ve tried, this :
-(void)hideBar {
viewController.navigationBarHidden = YES;
}
It is complaining that "navigationBarHidden" not found on object type UIViewController, obviously I would need to call the UINavigation controller instead, but that's where I'm stuck ...
Try:
[self.navigationController setNavigationBarHidden:YES];
within your TestViewController. A good place is in viewDidLoad
Also, while we're at it, remove the line [window addSubview:rootViewController.view];, you don't need it.
In Navigation controller's root view controller's method viewDidLoad write this -
[self.navigationController setNavigationBarHidden:TRUE];
in your case its TestViewController.

Orientation problem

i have created and ipad application.
i started off with window based application and added 2 view controllers(loginviewcontroller , detailviewcontroler ) . both have their own XIB'S. the added the loginviewcontroller object in in the appdelegate applicationdidfinishlaunch method , i wrote code to move back and forth between 2 views. Here is the code.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
Login *mylogin = [[Loginviewcontroller alloc] init];
[window addSubview:mylogin.view];
//detailview *tv=[[detailviewcontroller alloc] init];
// [window addSubview:tv.view];
[self.window makeKeyAndVisible];
return YES;
}
The problem is that the willrotatetointerfaceorientation method runs only from the loginviewcontroller class even if i am in the detailviewcontroller.
if i add the tickets view to the appdelegate then it runs the willrotatetointerfaceorientation method from detailviewcontroller , so in summary it runs the willrotatetointerfaceorientation method from only the object which was added to appdelegate.
how do i make the 2 view controllers run their respective willrotatetointerfaceorientation methods?
UIViewController has the method
(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
override this method in your both viewcontroller classes(i.e. (loginviewcontroller , detailviewcontroler )
write this code
-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
//write your code which you want to during rotation
return Yes;
}
The rotation messages are being sent only to the controller of topmost view. You should forward them manually in appropriate methods.