I am working on an application where I have a UINavigationController embedded inside a UITabBarController. The UINavigationController has a UITableView that transitions to a DetailController when a cell is tapped.
My question is: I select a cell on [TAB1] and transition to the DetailController. If I select [TAB2] and then go back to [TAB1] its still on the detail controller. Is there anyway when [TAB2] is selected that I can unwind/dismiss the DetailController on [TAB1] (i.e. so its showing the table view cells again).
My other line of thinking is that doing this would not leave the UI in the state the user left it in, i.e. after viewing the DetailController and pressing [TAB2] returning to [TAB1] would present the user with the cells in the UITableView. It feels better for the application to revert the DetailController if you leave the TAB, which is why I am asking...
NB: I present the *DetailController* via a push segue from theUITableViewCell.
You can implement UITabBarControlleDelegate and go back to root view of UINavigationController using popToRootViewControllerAnimated: when tab is changed.
Code example:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
UITabBarController *tabBarController = (UITabBarController *)self.window.rootViewController;
tabBarController.delegate = self;
return YES;
}
- (BOOL)tabBarController:(UITabBarController *)tabBarController
shouldSelectViewController:(UIViewController *)viewController
{
UIViewController *currentController = tabBarController.selectedViewController;
if ([currentController isKindOfClass:[UINavigationController class]])
[(UINavigationController *)currentController popToRootViewControllerAnimated:NO];
return YES;
}
Related
In my iPhone application I am using UITabbarController + UINavigationController, created in appDelegate.
My issue is as follows:
On screen- I have tableview. When one selects cell I am pushing the controller to screen-B. Now without poping it if I select another tab from tabbar, then view gets refreshed but navigationbar is not getting refreshed. It displays navigationbar of screen-Bscreen-B.
Used below code but nothing seems to solve my issue:
- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController
{
if ([viewController isKindOfClass:[UINavigationController class]])
{
[viewController.navigationController popToRootViewControllerAnimated:NO];
}
}
try this in appdelegate
UINavigationController *screen1=[[UINavigationController alloc] initWithRootViewController:controller1];
UINavigationController *Bscreen=[[UINavigationController alloc] initWithRootViewController:controller2];
NSArray *tabbararray=[[NSArray alloc] initWithObjects: screen1,Bscreen,nil];
self.TabControllerObj.viewControllers=allviewcontrollers;
and make tabbar as root viewcontroller of your window
I am assigning my view controller to my tab right after I create it. Is it possible to select the view that will show after the tab is clicked?
For eg
//user clicks tab 1
if(hasMessages)
//show view A
else
//show view B
Yes, it is possible. You need to set a delegate for your tab controller:
UITabBarController *tabBarController = (UITabBarController *)self.window.rootViewController;
tabBarController.delegate = self; // or whatever suitable class you have
This delegate needs to conform to the UITabBarControllerDelegate protocol.
In your delegate, implement tabBarController:didSelectViewController: and inside it, find out which view you want to present. Assuming your tab's root view controller is a navigation controller, then the delegate method implementation would be something like this:
- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController {
/* logic goes here */
[viewController pushViewController:someNewVC animated:YES];
}
New to iOS development, I've been following the tutorials on developer.apple.com, and am now adding functionality to those examples to further my knowledge.
The "second ios app" tutorial gives you a navigation controller based app. Extending this app, I want to have a tab bar controller as the first view controller.
So I now have the following setup:
All good. But there is code in the BirdsAppDelegate (a UIApplicationDelegate) which was relying on the navigation controller being the root view controller, so it can create and assign the "datacontroller" object.
This is the original code (before I added the tab bar controller):
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
UINavigationController *navigationController = (UINavigationController *)self.window.rootViewController;
BirdsMasterViewController *firstViewController = (BirdsMasterViewController *)[[navigationController viewControllers] objectAtIndex:0];
BirdSightingDataController *aDataController = [[BirdSightingDataController alloc] init];
firstViewController.dataController = aDataController;
return YES;
}
Now this code fails because it assumes the root view controller is the navigation controller.
I have updated the code so that it works - but in my opinion it is ugly, and would have to be changed every time I make a change to the view controller hierarchy:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions: (NSDictionary *)launchOptions
{
// Override point for customization after application launch.
UITabBarController *tabBarController = (UITabBarController *)self.window.rootViewController;
UINavigationController *navigationController = (UINavigationController *) [[tabBarController viewControllers] objectAtIndex:0];
BirdsMasterViewController *firstViewController = (BirdsMasterViewController*) [[navigationController viewControllers] objectAtIndex:0];
BirdSightingDataController *aDataController = [[BirdSightingDataController alloc] init];
firstViewController.dataController = aDataController;
return YES;
}
So my question is: What is the better way to do what I am doing in the code above, so that any changes to the hierarchy will not break the code?
How do I programmatically access the view controller I am after in the application delegate, so that I can create and assign it's BirdSightingDataController object?
Thanks!
You can loop the [navigationController viewControllers] array looking for an instance of BirdsMasterViewController... Using [obj isKindOfClass:[BirdsMasterViewController class]].
You don't even need that code at a all. If you just want to change the controller, go to the storyboard and select the viewController you want to change to a TabBarController. In the Editor menu, there is an option for "Embed In", the choices are TabBar and Navigation controllers.
I always start with a single view application template. There is no code in the "application didFinishLaunchingWithOptions:" method,(except to return YES). You can set any viewController as your initial view in the storyboard, by setting the is initial View Controller check box, or just dragging the arrow to the viewController you want as your initial view.
I have a tabBarController that I add by placing the following code into:
AppDelegate.h:
...
UITabBarController IBOutlet *tabBarController;
}
#property (nonatomic, retain) IBOutlet UITabBarController *tabBarController;
AppDelegate.m:
...
[self.window addSubview:tabBarController.view];
[self.window makeKeyAndVisible];
[tabBarController setDelegate:self];
I then use the following code to present a modal barcode scanning View Controller:
- (void)tabBarController:(UITabBarController *)tbc didSelectViewController:(UIViewController *)vc {
// Middle tab bar item in question.
if (vc == [tabBarController.viewControllers objectAtIndex:2]) {
ScanVC *scanView = [[ScanVC alloc] initWithNibName:#"ScanViewController" bundle:nil];
// set properties of scanView's ivars, etc
UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:scanView];
[tabBarController presentModalViewController:navigationController animated:YES];
[navigationController release];
[scanView release];
}
}
When it does actually get presented I think this method isn't visually appealing, because when I dismiss the modal view I am brought back to an empty view.
A lot of barcode scanning applications or applications that simply display an image picker for example; do this quite successfully. I'm just wondering what kind of implementation they would use in order to achieve such an effect.
This is a screenshot of an application called Path, which has the exact same functionality I'm after:
I also noticed that in these applications, if you are on any other tab bar item other than the middle one let's say, and you click on the tab bar item that presents the modal view, once it gets dismissed it doesn't actually bring them back to an empty view it dismisses like normal, however the actual tab bar item that presents the modal view is never selected. I would be happy with this type of functionality if that's the only way to implement this type of effect.
Any help would be greatly appreciated as I've been stuck in this for quite some time. Also I'm not even sure whether it's the right way to put all of this code in my AppDelegate in order for the View Controller to be presented as a modal. It all seems, just, wrong.
Not entirely what I'm after, but I think I can move forward from this:
http://idevrecipes.com/2010/12/16/raised-center-tab-bar-button/
When you dismiss the modal view controller, tell the tab bar to select whatever tab was originally selected.
- (void)dismissModalViewControllerAnimated:(BOOL)animated
{
// do whatever you need to do when dismissing
// savedTabIndex is an int ivar
// tabBarController is a reference, set when showing the modal view
[[self tabBarController] setSelectedIndex:savedTabIndex];
}
You would have to save the original tab bar selection in a variable at the start of tabBarController:didSelectViewController:.
- (void)tabBarController:(UITabBarController *)tbc
didSelectViewController:(UIViewController *)vc
{
// Save the tab bar index (if it's not the photo tab)
if ([tabBarController selectedIndex] != 3]) {
savedTabIndex = [tabBarController selectedIndex];
}
}
There could be mistakes in this code, I just typed it without testing.
I found a really easy solution by playing around UITabBarControllerDelegate--I only tried this in iOS 7 though.
First, subclass UITabBarController, make it its own UITabBarControllerDelegate, and create a property that'll hold a reference to the tab you want to launch a modal with. In my app, it's called the "Sell" tab.
#property (strong, nonatomic) UIViewController *sellTab;
Then, in your init method, just create that view controller and add it to the tabs.
_sellTab = [[UIViewController alloc] init];
_sellTab.title = #"Sell";
self.viewControllers = #[homeTab, historyTab, _sellTab, bookmarksTab, profileTab];
Now here's where the magic is: override the following tab bar controller delegate methods. Code is pretty self-explanatory.
#pragma mark - Tab bar controller delegate
- (BOOL)tabBarController:(UITabBarController *)tabBarController shouldSelectViewController:(UIViewController *)viewController
{
return viewController != self.sellTab;
}
- (void)tabBar:(UITabBar *)tabBar didSelectItem:(UITabBarItem *)item
{
if (item == self.sellTab.tabBarItem) {
[self presentViewController:[[UINavigationController alloc] initWithRootViewController:[[PostAdViewController alloc] init]] animated:YES completion:nil];
}
}
This will launch a modal which, upon dismissal, shows the same tab you were in before launch.
You shouldn't present a modal view, when the user clicks on a tab bar item.
You could instead present a modal view from within a view that's presented by one of the tabs.
Or, if you just have a single main view and the scan view you want to present modally, you should just use a button to present the scan view from within your main view. You could for instance use a toolbar with a single button in it, instead.
When I receive a Push Notification, I want to jump to the middle tab on the tabbar and reset the tab (it has multiple table views that the user may have navigated into). I am able to jump to the tab using tabBarController.selectedIndex = 1; ... but how do I reset to the first view?
Implement UITabBarControllerDelegate on a controller class (perhaps your app delegate) and implement tabBarController:didSelectViewController: to pop all pushed view controllers off the navigation stack:
- (void) tabBarController:(UITabBarController *) tabBarController didSelectViewController:(UIViewController *) viewController {
if([viewController isKindOfClass:[UINavigationController class]])
{
[(UINavigationController *)viewController popToRootViewControllerAnimated:YES];
}
}