How to open specific ViewController from AppDelegate? - ios

I know this question has been asked many times. But I am a newbie and I was not able to find any solutions to help me in my condition. Here is a brief explanation:
I have an app with a UITabBarController as root.
Inside the app delegate I check if user is already logged in. If yes I will open root controller.
self.window.rootViewController = [[UIStoryboard storyboardWithName:#"Main" bundle:[NSBundle mainBundle]] instantiateInitialViewController];
My application is working fine. But now I need to implement notification. When I get notification content inside didFinishLaunchingWithOptions:
NSDictionary *notificationPayload = launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey];
NSString *messageType = [notificationPayload objectForKey:#"messageType"];
Now if there is a message:
if (messageType.length > 0 )
{
//Here based on message I need to open different tabs of TabBarViewController
UITabBarController *tabBarController = (UITabBarController *)self.window.rootViewController;
//RootTabBarViewController *listingVC = [[RootTabBarViewController alloc] init];
[(UINavigationController *)self.window.rootViewController pushViewController:tabBarController animated:YES];
}
This piece of code above did not work for me.
And also I don't know how to open different tabs and give value to their badges form here. It always navigate to the first index tab with these code. I have seen other answers say:
self.tabBarController.selectedIndex = 2;
But did not work for me. I have implemented an UITabBarController class and I can set value for the each tab item badges from there but I get my notificationPayLoad in AppDelegate.

As you told that you are using TabBarController with storyboard. Then why are you initialising again? You can just do as following
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
if([[NSUserDefaults standardUserDefaults]boolForKey:#"Selected"])
{
[[NSUserDefaults standardUserDefaults]setBool:NO forKey:#"Selected"];
UITabBarController *tabController = (UITabBarController*)self.window.rootViewController;
tabController.selectedIndex=1;
}else{
[[NSUserDefaults standardUserDefaults]setBool:YES forKey:#"Selected"];
UITabBarController *tabController = (UITabBarController*)self.window.rootViewController;
tabController.selectedIndex=0;
}
return YES;
}
Here I am just showing a demo code for accessing TabbarController at different selectedIndex within didFinishLaunching.

Assuming that you have designed(drag n dropped UITabBar/ UITabBarController) in storyboard.
if (messageType.length > 0 )
{
//Here based on message I need to open different tabs of TabBarViewController
NSUInteger indexOfRequiredTab = 2;// pass the index of controller here
id rootObject = self.window.rootViewController;
if ([rootObject isKindOfClass:[UITabBarController class]]) {
UITabBarController *tabBarControllerObject = (UITabBarController *)rootObject;
if (indexOfRequiredTab != NSNotFound && indexOfRequiredTab < tabBarControllerObject.tabBar.items.count) {
tabBarControllerObject.tabBar.items[indexOfRequiredTab].badgeValue = #"2"; //to set badge value
tabBarControllerObject.selectedIndex = indexOfRequiredTab; //Setting this property changes the selected view controller to the one at the given index in the viewControllers array
}
}
}
Views designed on story board are already initialized, we can access them using the IBOutlet and change their properties.

Related

Pushing ViewController from app delegate

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

UISplitView with Multiple Detail Views (with Storyboard)

I've been trying to create a version of this code using a storyboard:
I want to be able to switch between two different detail views, depending on the cell selected in the navigation table. I've tried to implement this by creating a SplitViewManager with a custom setter method that swaps out the detail views each time a different cell is selected. This is the same approach that Apple's sample code uses. The SplitViewManager follows the delegate.
I think my issue is that I haven't connected my splitViewController.delegate to anything, so I can't assign the splitViewManager to anything either. But I can't figure out what I would even connect the delegate to in the storyboard. Please let me know if I'm being an idiot here (almost definitely). Thanks!
My code is below:
DFMAppDelegate.m:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.splitViewController = (UISplitViewController *)self.window.rootViewController;
self.splitViewManager = (DFMSplitViewManager *)self.splitViewController.delegate;
NSLog(#"split view controller: %#", self.splitViewController); // not null
NSLog(#"split view controller delegate: %#", self.splitViewController.delegate); // is null
NSLog(#"split view manager: %#", self.splitViewManager); // is null.
// But i'm not sure how to assign splitViewController.delegate or splitViewManager in the storyboard.
return YES;
}
DFMSplitViewManager.m:
- (void)setDetailViewController:(UIViewController<SubstitutableDetailViewController> *)detailViewController
{
self.detailViewController = detailViewController;
// Update the split view controller's view controllers array.
// This causes the new detail view controller to be displayed.
UIViewController *navigationViewController = [self.splitViewController.viewControllers objectAtIndex:0];
NSArray *viewControllers = [[NSArray alloc] initWithObjects:navigationViewController, self.detailViewController, nil];
self.splitViewController.viewControllers = viewControllers;
}
DFMMasterViewController.m:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
DFMAppDelegate *appDelegate = (DFMAppDelegate *)[[UIApplication sharedApplication] delegate];
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
if (indexPath.row == 0) {
NSLog(#"clicked cell 1");
DFMDetailViewController *detailViewController = [storyboard instantiateViewControllerWithIdentifier:#"ViewController"];
[appDelegate.splitViewManager setDetailViewController:detailViewController];
}
else {
NSLog(#"clicked cell 2");
DFMDetailCollectionViewController *detailCollectionViewController = [storyboard instantiateViewControllerWithIdentifier:#"CollectionViewController"];
[appDelegate.splitViewManager setDetailViewController:detailCollectionViewController];
}
}
Turns out you can use the interface builder to add NSObjects to View Controllers. Once I did that, I changed the NSObject's class to DFMSplitViewManager, set it as the SplitViewController's delegate, and it was pretty straight forward from there.
I am facing exactly the same problem as yours. Don't know wether you find out the solution or not, here is the solution I found.
Use following code in your AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
UISplitViewController *splitViewController = (UISplitViewController *)self.window.rootViewController;
self.detailViewManager = [[DetailViewManager alloc] init];
self.detailViewManager.splitViewController = splitViewController;
self.detailViewManager.detailViewController = splitViewController.viewControllers.lastObject;
splitViewController.delegate = self.detailViewManager;
if ([splitViewController respondsToSelector:#selector(setPresentsWithGesture:)])
[splitViewController setPresentsWithGesture:YES];
return YES;
}
The rest of code is the same as what Apple has provided.
Basically, self.detailViewManager is our split view controller, when you select cell in table, self.detailViewManager will reset the detail view (if I'm not wrong). I'm new to Xcode, so anyone please correct me if I'm wrong.
Here is the solution link, answered by hallmark.

self.navigationController.tabBarController.selectedIndex not working

I have a UITabBarController which containing 4 different UIViewControllers.
On first tab there is a UINavigationViewController which contains its child UIViewController. without tapping the tab bar, i want to take user on second tab. For this I tried:
self.navigationController.tabBarController.selectedIndex = 1;
But it's not working.
ignore any mistake i am a newbie.
Thank you.
if set delegate
Try this:
self.selectedIndex = 1;
You are the tabBarController :)
Do the following, before trying to change tab:
UITabBarController *tab = self.tabBarController;
if (tab) {
NSLog(#"I have a tab bar");
[self.tabBarController setSelectedIndex: 1];
}
else {
NSLog(#"I don't have one");
}
Try moving your tab switch code to viewDidLoad
I verified that this works on the built in Tabbed Application template project in xcode 4
- (void)viewDidLoad
{
[super viewDidLoad];
self.tabBarController.selectedIndex = 1;
}
I'd also take a look at your AppDelegate class for something along the lines of this
self.tabBarController = [[[UITabBarController alloc] init] autorelease];
self.tabBarController.viewControllers = [NSArray arrayWithObjects:viewController1, viewController2, nil];
self.window.rootViewController = self.tabBarController;
If you don't see these lines anywhere then you'll want to find where else in your code the UITabBarViewController is being initialized and use that object pointer instead.
for calling the tabbarcontroller.selectedindexyou must have to find out the tabbarcontroller from the navigation controller if you dont use push on navigationController just use topViewController property of navigationController to get the instance of your tabBarController and assign it to UITabBarController and us the selectedIndex and this will work fine
just try
self.tabBarController.selectedIndex = 1
we should put follow section in viewDidAppear.
- (void)viewDidAppear:(BOOL)animated
{
NSArray *ary = [self.tabBarController viewControllers];
UIViewController *vc = ary[3];
[self.tabBarController setSelectedIndex:3];
[self.tabBarController setSelectedViewController:vc];
}
If you are presenting a new controller (different from the one that's currently on screen) you should set the index in the completion block of the function to present your view controller. If you set it before it's presented, it may not work.
In my case I've embedded the tab controller inside a nav controller. I present the new nav controller newNavContoller, and once it's presented I set the index in my tab controller tvc:
currentViewController?.presentViewController(newNavController, animated: true, completion: {tvc.selectedIndex = selectedIndex})

Disable/Enable tabs in UITabBarController in storyboard-based app

I've built my app with storyboards and all views are managed by a tabbarcontroller.
So on launch (I'm only working on the iPad UI currently) it does this:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad)
{
UITabBarController *tabBarController = (UITabBarController *)self.window.rootViewController;
UISplitViewController *splitViewController = [tabBarController.viewControllers objectAtIndex:0];
UINavigationController *navigationController = [splitViewController.viewControllers lastObject];
splitViewController.delegate = (id)navigationController.topViewController;
UINavigationController *masterNavigationController = [splitViewController.viewControllers objectAtIndex:0];
ProductionMasterViewController *controller = (ProductionMasterViewController *)masterNavigationController.topViewController;
controller.managedObjectContext = self.managedObjectContext;
}
}
I want to be able to enable or disable the tabs in the tabBarController based on user input (so, for example, an item needs to be selected in the first tab in order to access the second and third tabs, which are disabled by default)
What I'm not clear on is how to access the tabs in order to enable/disble them. Would I create an instance of the appdelegate and then do something like
AppDelegate *d = (AppDelegate *)[[UIApplication sharedApplication] delegate];
UITabBarController *tabs = (UITabBarController *)[d.window rootViewController];
[[[[tabs tabBar] items] objectAtIndex:2] setEnabled:YES];
[[[[tabs tabBar] items] objectAtIndex:3] setEnabled:YES];
[[[[tabs tabBar] items] objectAtIndex:4] setEnabled:YES];
(That kinda seems like it should work but it also seems fairly gross.)
Since you're using a Storyboard based app, I'd assume you have the UITabBarController defined in the storyboard as the root controller. Incidentally, you can also retrieve it by identifier instead of walking from the window to the root view controller.
Restricting which tabs are selectable, is achieved by setting a delegate of the UITabBarController (i.e. one that conforms to UITabBarControllerDelegate).
In the delegate, you can implement these two methods:
– tabBarController:shouldSelectViewController:
– tabBarController:didSelectViewController:
Likely, you just need the first to restrict (inhibit) selection, until your workflow is ready.
Another approach, is to set the "viewControllers' property on the tab bar controller, each time a milestone is passed. At each milestone, you set a more expansive array of view controllers into this property, which will open up the selection of the additional tab item.
SWIFT 3
(expanded for ease of understanding)
let arrayOfTabBarItems = tabBarController?.tabBar.items
if let barItems = arrayOfTabBarItems, barItems.count > 0 {
os_log("barItems.count is now ", barItems.count)
tabBarItem0 = barItems[0]
tabBarItem0.isEnabled = true
tabBarItem1 = barItems[1]
tabBarItem1.isEnabled = true
tabBarItem2 = barItems[2]
tabBarItem2.isEnabled = true
tabBarItem3 = barItems[3]
tabBarItem3.isEnabled = true
tabBarItem4 = barItems[4]
tabBarItem4.isEnabled = true
}
This can be used in your viewWillAppear on each tab controller. Check your rules against this and restrict each tab accordingly.
(more concise method)
let arrayOfAllTabBarItems = tabBarController?.viewControllers
if let tabBarArray = arrayOfAllTabBarItems, tabBarArray.count > 0 {
for x in 0...tabBarArray.count-1 {
let tabBarItem = tabBarArray[x]
if tabBarItem.title != nil {
if tabBarItem.title == "Tab1" || tabBarItem.title == "MyTab" || tabBarItem.title == "Tab2Check" {
tabBarItem.tabBarItem.isEnabled = !(isMyRuleTrue!)
}
}
}
}

Setting up a UINavigation Controller on a UIViewController as rootView

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.

Resources