I would like to open a specific UIViewController depending on an id I get from a URL scheme.
For exemple myapp://news/285 would open the UIViewController "news" displaying this specific one.
I get notified here :
- (BOOL)application:(UIApplication *)application
openURL:(NSURL *)url
sourceApplication:(NSString *)source
annotation:(id)annotation
{
if ([[url scheme] isEqualToString:#"myapp"]) {
NSString *urlString = url.absoluteString;
...
return YES;
}
return NO;
}
But how am I supposed to push the UIViewController ? I can't access to the navigation controller from here.
(I was thinking about sending a notification with NSNotficationCenter to my home page with the id, and pushing the UIViewController from there. What do you think ?)
Thx
You know you view setup. So when you rootViewController is a UINavigationController then you can just use:
UINavigationController *navCon = (UINavigationController*)self.window.rootViewController;
[navCon pushViewController:myPushedVC animated:NO];
If not then maybe it is easier to go with the Notification approach
You could achieve that sending a notification through NSNotificationCenter. Your view controller could catch that notification and push the other view controller.
If you are using a Tab bar, then use following code.
UINavigationController *navigationController = (UINavigationController *) tabBar_Controller.selectedViewController;
you can set the selected view controller as per your requirement.
If you are not using a tabbar
UINavigationController *navigationController = (UINavigationController*)self.window.rootViewController;
Now you can push to any view.
Related
I have tabbar controller having three tab, attached three navigation controller I want to go to second controller of navigation controller just like whatsapp. I handled successfully for background state, but for not running state. Below is my code in didfinishlaunch delegate method of uiapplication.
if (launchOptions != nil) {
// Launched from push notification
NSDictionary *notification = [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];
[self performSelector:#selector(notificationObserverAfterDelay:) withObject:notification afterDelay:2.0];
}
-(void)notificationObserverAfterDelay:(NSDictionary *)userInfo
{
[[NSNotificationCenter defaultCenter] postNotificationName:#"RefreshUIChatNotRunningState" object:self userInfo:userInfo];
}
Nazish Ali,
Sending out notification after delay of 2 seconds is really not a good idea.
Declare a method which accepts your userInfo dictionary as argument in your viewController having tab bar.
lets call it as,
YourViewController.h
-(void)appStartedFromPushNotification : (NSDictionary *)userInfo;
YourViewController.m
-(void)appStartedFromPushNotification : (NSDictionary *)userInfo {
//now the control has reached yourViewController with tab bar
//change the tab bar selection and perform segue and load the next viewController and use prepareforSgue to pass the data
}
Now why YourViewController only why not the actualViewController which anyway needs to be loaded ??? Reason you dont want mess with navigation stack manually. Because YourViewController is the child of UINavigationController and UINavigationController is your rootView controller YourViewController will be loaded automatically. So just access it and ask it to perform the tab change and load viewControllers so your navigation stack will continue to be stable :)
finally in Appdelegate,
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
NSDictionary *userInfo = [launchOptions valueForKey:UIApplicationLaunchOptionsRemoteNotificationKey];
if (launchOptions) {
[self handleReceiveRemoteNotification: userInfo];
}
return YES;
}
Declare a method in AppDelegate.m and access the YourViewController instance and handover data to it thats all :)
- (void) handleReceiveRemoteNotification:(NSDictionary *)userInfo{
UINavigationController *rootViewController = (UINavigationController *)self.window.rootViewController;
for(UIViewController *viewController in rootViewController.viewControllers){
if([viewController isKindOfClass:[YourViewController class]]) {
[viewController appStartedFromPushNotification:userInfo];
}
}
That should do the job :) Happy coding :)
I've got an app that is a list of itens in a table view and displays a detail view controller to every item on the table view. It also implements MMDrawerController (root view controller) as a side menu with storyboard.
I'm deep linking my app and using application open URL source application annotation method from App Delegate to handle it. So, I want to push a detail view controller from this method using MMDrawerController and I'm having some trouble.
Have a look in some code:
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
DetalheRestauranteViewController *vc = [storyboard instantiateViewControllerWithIdentifier:#"detalheRest"];
MMDrawerController* drawerController = (MMDrawerController *)self.window.rootViewController;
// If I use this nothing happens
[drawerController.centerViewController.navigationController pushViewController:vc animated:YES];
// If I use this nothing happens
[((MMDrawerController *)self.window.rootViewController).centerViewController.navigationController pushViewController:vc animated:YES];
// If I use this I got the unrecognized selector error
[(UINavigationController*)self.window.rootViewController pushViewController:vc animated:NO];
return YES;
}
Can someone help me with this?
I have found that with deep linking and the new iOS9 menu shortcuts that a small delay is often needed to display or manipulate UI Components.
I would try moving all of your UI/MMDrawer code to its own method. Then when openURL is called in your app delegate, call your new method on a delay.
So in ObjC it would look something like this:
[self performSelector:#selector(showDetailView) withObject:nil afterDelay:0.3];
do you solve this problem?
I tried many times. Finally I found it works fine for me today:
[((UINavigationController *)((MMDrawerController *)self.window.rootViewController).centerViewController) pushViewController:viewController animated:YES];
or
MMDrawerController *mvc = (MMDrawerController *)self.window.rootViewController;
UINavigationController *nvc = (UINavigationController *)mvc.centerViewController;
[nvc pushViewController:vc animated:YES];
These two kinds of writing are the same.
Here vc is one DetalheRestauranteViewController instance.
I am using a storyboard in Xcode 5, which appears as so:
My requirement is to push a ViewController (VIEW1 or VIEW2) into view from the app delegate. Essentially it should not matter what view is presently on the screen -- I would just like to make a ViewController appear when the app delegate picks up an external event.
In order to try and achieve this, I have property references to both the TabBarCtrl-Products and NavCtrl-ProductA in my app delegate.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
_tabBarProducts = [[UIStoryboard storyboardWithName:#"Main" bundle:nil] instantiateViewControllerWithIdentifier:#"sidTabBarProducts"];
NSArray *tabvcs = _tabBarProducts.viewControllers;
for (id controller in tabvcs){
if ([controller isKindOfClass:[VCNavControl_ProductA class]]) {
_navControllerProductA = controller;
break;
}
}
return YES;
}
The app delegate method to push VIEW2 is:
-(void)showVCVIEW2
{
VC_V2 *targetvc = nil;
targetvc = [[UIStoryboard storyboardWithName:#"Main" bundle:nil] instantiateViewControllerWithIdentifier:#"sidView2"];
[[AppDelegate sharedInstance].navControllerProductA pushViewController:targetvc animated:NO];
}
This works OK when VIEW1 is showing at the time showVCVIEW2 is called, however it does not work when ViewCtrl-ProductB is showing. I can see that the new instance of targetvc has been added to the AppDelegate _navControllerProductA's stack, however it does not display.
(Regarding the setting of the the app delegate's rootViewController, I set this to _tabBarProducts after the VC-Splash and VC-Setup ViewCtrls have finished).
I would appreciate very much if anyone can give me an idea on how to achieve this. I suspect my problems stem from having a NavCtrl in a TabBarCtrl, but I do not know a way around this.
Your problem is that the navigation controller you are pushing on is not in the view hierarchy.
You instead could try setting the tabBarController's selected index like this:
[self.tabBarController setSelectedIndex:1];
In my AppDelegate, I present a ViewController if the user types in my custom url scheme. It works as my ViewController is presented. However, I need to detect if my ViewController was pushed from App delegate. What is the best method or course of action to do this? I only want to detect if it comes from AppDelegate and no where else.
- (BOOL)application:(UIApplication *)application
openURL:(NSURL *)url
sourceApplication:(NSString *)sourceApplication
annotation:(id)annotation {
// attempt to extract a token from the url
ViewController *controller = [[ViewController alloc] initWithNibName:#"ViewController" bundle:nil];
[self.navigationController pushViewController:controller animated:YES];
EDIT:To clarify what I am trying to do better, when appdelegate presents my viewcontroller I need to be able to detect inside my viewcontroller that it appeared because of the method inside app delegate. So kind of like this
ViewController
-(void) ViewDidLoad{
if (this controller was presented from App delegate){
do this
}
else{
do nothing
}
Do it simply like this :
- (BOOL)application:(UIApplication *)application
openURL:(NSURL *)url
sourceApplication:(NSString *)sourceApplication
annotation:(id)annotation {
ViewController *controller = [[ViewController alloc] initWithNibName:#"ViewController" bundle:nil];
[self.navigationController pushViewController:controller animated:YES];
// here you know that your controller is pushed from the AppDelegate, then you can do other things just after the push
[controller callControllerPublicMethod];
}
And when you push it in an other controller, simply do not do anything.
Why not just write your own custom initializer method in the ViewController and when called from the app delegate set a parameter to true, and everywhere else set it to false.
I had a tabBarApplication that I have as a template which has a UINavigation template on each tab.
I want to use that sample (sort of) and convert it into a single UIViewController in a nother application. I have presented 2 pieces of code, the first one is my template and the latter is what I am trying to make. Could anyone please give me some hints or help with how to that right? I keep getting errors, but they do not make sense to me as the tabBarApp does not need it a viewController declared.
First Code (the example):
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[self setupFetchedResultsController];
if (![[self.fetchedResultsController fetchedObjects] count] > 0 ) {
NSLog(#"!!!!! ~~> There's nothing in the database so defaults will be inserted");
[self importCoreDataDefaultRoles];
}
else {
NSLog(#"There's stuff in the database so skipping the import of default data");
}
// The Tab Bar
UITabBarController *tabBarController = (UITabBarController *)self.window.rootViewController;
// The Two Navigation Controllers attached to the Tab Bar (At Tab Bar Indexes 0 and 1)
UINavigationController *personsTVCnav = [[tabBarController viewControllers] objectAtIndex:0];
UINavigationController *rolesTVCnav = [[tabBarController viewControllers] objectAtIndex:1];
return YES;
}
Second Code (what I am trying to make):
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[self setupFetchedResultsController];
if (![[self.fetchedResultsController fetchedObjects] count] > 0 ) {
NSLog(#"!!!!! ~~> There's nothing in the database so defaults will be inserted");
[self importCoreDataDefaultRoles];
}
else {
UIViewController *mainViewController = (UIViewController *)self.window.rootViewController;
UINavigationController *readingsTVCnav = [[mainViewController viewController] objectAtIndex:0];
// Override point for customization after application launch.
return YES;
}
The fetching parts of the code are related to Core Data which already set up and working.
The reason for this change is that I want to have a plain view controller set up as the initial screen rather than the tabBarConfiguration.
Cheers Jeff
EDIT: I have added an image for clarity
From what you describe and depict in your image, you have the navigation controller set up as your app's root view controller. So you can access it (if you need to) in your app delegate's didFinishLaunchingWithOptions: method as follows:
UINavigationController *navController = (UINavigationController *)self.window.rootViewController;
Although for your app I don't think there's any reason you need to reference the nav controller from your app delegate. All the setup you need (with the exception of your code data code, which you say is already working) can be handled through the storyboard.
In your nagivationController's root viewController (the one with all the buttons) you should set up a segue from each button to the approperiate viewController in your storyboard. Bure sure to set then up as "push" segues so that it will push the view controller onto your navigationController's navigation stack. If there is any particular setup you need to do for the child view controller when the segue happens, you can implement the prepareForSegue: method like this:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"ShowMyViewController"]) {
MyViewController *vc = (MyViewController*)segue.destinationViewController;
vc.title = #"My Title";
vc.someProperty = #"Some Value";
}
}
You can (and should) identify each of your segues in the storyboard with a unique identifier so that you can identify them when this method is called.