How to find out if a UIViewController is the root view controller? - iphone

basically I want to know if the view controller I'm in is the root view controller or not.
If its not I want to put a button in the nav bar that says "back" (as if it were a proper back button - this bit I know how to do).
Before you ask, I have removed all the titles from my view controllers - I didn't want them to show up on my navigation bar... its very complicated - but this means that when I go through my navigation stack none of the pushed view controllers have a back button. :/
Thanks
Tom

if ( self != [self.navigationController.viewControllers objectAtIndex:0] )
{
// Put Back button in navigation bar
}

You can also try:
if (self.navigationController.viewControllers.count == 1) {
NSLog(#"self is RootViewController");
}

Here's a swift version:
// Only works if checking from within the NavigationController:
navigationController?.viewControllers.first == self
// Works if you only have a reference to the NavigationController:
navigationController?.topViewController == navigationController?.viewControllers.first

Related

UITabBar MoreViewController image in UITableView disappears after programmatically selecting it

I'm trying to select one of my UINavigationControllers in the UITabBar.viewControllers array.
I previously tried it with setting the UITabbarController.selectedIndex, but the Apple documentation says:
"To select the More navigation controller itself, you must change the value of the selectedViewController property instead."
AppDelegate *appDelegate = (AppDelegate*)[UIApplication sharedApplication].delegate;
UINavigationController *navController = [appDelegate.objTabBarController.viewControllers objectAtIndex:5];
[appDelegate.objTabBarController setSelectedViewController:navController];
Doing so is fine but when I change back to the MoreViewController's list view, the icon on the left side is gone and won't come back.
Everything ok, when selecting it with the finger.
Bug when selecting programmatically -> image is gone
Any suggestions what I'm doing wrong?
Best regards,
Steve
Selecting the More Navigation Controller
To select the moreNavigationController, you must set the selectedViewController:
self.tabBarController.selectedViewController = self.tabBarController.moreNavigationController;
The reason for that is simple: The moreNavigationController is not inside the viewControllers of the tab bar controller, so it can't be selected by index.
To select one of the "more" controllers directly, you can either set the selectedViewController property:
self.tabBarController.selectedViewController = viewController5;
Or you can set the selectedIndex:
self.tabBarController.selectedIndex = 5;
Fixing the Disappearing Tab Bar Image
The disappearing tab bar image is caused by using a navigation controller (for the settings) inside a navigation controller (the moreNavigationController generated by the tab bar controller). To fix it, there are two solutions:
Don't add navigation controllers for the more section, but add the controllers directly. The controller structure would look like this, assuming the controllers in all tabs need navigation:
tabBarController
+ navigationController
+ viewController0
+ navigationController
+ viewController1
+ navigationController
+ viewController2
+ navigationController
+ viewController3
+ viewController4
+ viewController5
Set the tabBarItem of the settings controller on its navigation controller (you only need to do this once):
UINavigationController *settingsNavigationController = [appDelegate.objTabBarController objectAtIndex:5];
UIViewController *settingsRootController = settingsNavigationController.viewControllers[0];
settingsNavigationController.tabBarItem = settingsRootController.tabBarItem;
I ran into this same problem. It seems that the navigation controller was somehow losing track of the view controller. In my case, the navigation controller whose image was disappearing was at index 7 in the tabBarController. The navigation controller was supposed to contain a viewController of class SettingsViewController. But sometimes it lost it.
To fix it, I had to add code to two classes -- the app delegate and SettingsViewController. In the app delegate:
-(void) fixSettingsNavigationController {
UITabBarController* tab = self.tabBarController;
NSArray* vcs = tab.viewControllers;
NSInteger nVCs = vcs.count;
if (nVCs > 7) {
UIViewController* settingsContainer = vcs[7];
UINavigationController* settingsContainerNav = (UINavigationController*)settingsContainer;
if ([settingsContainerNav isKindOfClass: [UINavigationController class]]) {
NSArray* settingsNavVCs = settingsContainerNav.viewControllers;
NSInteger count = settingsNavVCs.count;
SettingsViewController* svc = self.settingsViewController;
if (!count) {
// The container navigation controller has lost track of the settings view controller.
settingsContainerNav.viewControllers = [NSArray arrayWithObject:svc];
}
}
}
}
And in SettingsViewController:
-(void) viewWillDisappear:(BOOL)animated {
MyAppDelegate* appDel = [[UIApplication sharedApplication] delegate];
[appDel fixSettingsNavigationController];
[super viewWillDisappear: animated];
}
I ran to the very similar situation, where I want to select a View controller inside MoreViewController programmatically.
As you said in your question, apple don't allow selecting a view controller that is inside MoreViewController (Or not visible in tabbar).
In my case, I don't need default "More" features of reordering tabs in tabbar.
So, I did following things
Created a UITableViewControlelr as my 5th tab, set its Title to more and custom image (Similar to default more image).
In my UITableViewController I listed items that corresponds to ViewControllers. Tapping on which show respective view controller.
When i need to show any view controller that is inside my UITableViewController programatically, i simply set a flag and push my UITableViewController, and in viewDidAppear method of my UITableViewController push desired view controller.
This will also show back button on programatically opened view to list of MoreViewController

Navigation Item not appearing on View Controller

I apologize I am new to iphone programming.
I have created a Master-Detail Iphone application (so Navigation Controller came with the project). I segue to a new view controller I created through a UIBarButtonItem on the masterviewcontroller. However unlike the detailviewcontroller (that came with the project) I can not seem to get the navigationitem (or navigationbar?) to display on the view even though it appears in the scene list of my storyboard.
Heres some code and a screenshot:
In my masterviewcontroller.m viewdidload() function
UIBarButtonItem *settingsButton = [[UIBarButtonItem alloc] initWithTitle:#"Settings" style:normal target:self action:#selector(goToSettings:)];
self.navigationItem.leftBarButtonItem = settingsButton;
in my masterviewcontroller.m
- (IBAction)goToSettings:(id)sender{
[self performSegueWithIdentifier:#"SettingsSegue" sender:self];
}
I tried adding a title to the navigationitem during the viewDidLoad function of the new viewcontroller.m class i created (mentioned in this Link but it didn't work)
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
self.navigationItem.title = #"Settings";
self.navigationItem.leftBarButtonItem = self.editButtonItem;
}
But it still shows up in my storyboard ( it shows up in the list under the scene but not in the display of the view)
So my question is why is it now showing up and how do I get it to? I want a back button like my detail view controller that came with the master-detail project.
EDIT#1
I have added a check for whether navigation controller is nil and it is not nil (the if statement is never entered) I also tried changing the navigation item to back and removing and none has worked.
if(self.navigationItem == nil)
{
[ self.navigationItem init];
}
self.navigationItem.title = #"Settings";
self.navigationItem.leftBarButtonItem = self.navigationItem.backBarButtonItem;
Now that I have enough reputation to show an image I can show that the navigation item shows up in the list but doesnt show up on the view
The Navigation item did not show up because of the "style" of my segue.
The segue that moved the scene from the master view controller to the settings view controller was set to 'modal'. It has to be set to 'push'. This is done from the storyboard on the utilities pane
You have to change your Segue Style by doing this:
Select the Segue in your Storyboard;
Go to the Attributes inspector (on the right pane tabs);
Change the Style attribute to Push.
It will make the screen roll from right to left and the Navigation bar will appear sliding.
With Modal, the screen come from bottom to the top of the screen and it will not let the Navigation bar appears.
The problem in my case was that the Navigation controller wasn't the "initial View Controller"
So I had to do this
You have verified that your navigation controller listed in the viewDidLoad method is not nil?
Assuming that you do correctly have a self.navigationItem that is non-nil, it appears that you are incorrectly setting your self.navigationItem.leftBarButtonItem to an editButtonItem, instead try removing this all together!
If removing it does not solve the problem try:
self.navigationItem.leftBarButtonItem = self.navigationItem.backBarButtonItem;
Please check if your view controller is the root view controller of the navigation controller. You cant directly present a view controller as a modal view controller. You have to make your view controller as the root view controller of a navigation controller and then present your navigation controller as the modal view controller.
// Create the root view controller for the navigation controller
// The new view controller configures a Cancel and Done button for the
// navigation bar.
YourViewController *addController = [[YourViewController alloc]
init];
// Configure the YourViewController.
// Create the navigation controller and present it.
UINavigationController *navigationController = [[UINavigationController alloc]
initWithRootViewController:addController];
[self presentViewController:navigationController animated:YES completion: nil];
You need to have a Navigation Controller and a View Controller.
"The navigation controller manages the navigation bar at the top of the interface and an optional toolbar at the bottom of the interface. The navigation bar is always present and is managed by the navigation controller itself, which updates the navigation bar using the content provided by its child view controllers. When the isToolbarHidden property is false, the navigation controller similarly updates the toolbar with contents provided by the topmost view controller."
https://developer.apple.com/documentation/uikit/uinavigationcontroller
The Navigation Item also appears in the View Controller Scene you can look for it there and edit it too

iphone toolbar showing view

ok, i cant find the method called by UIViewController that presents a new view.
i have an app based on UINavigationController. Once i come to a view showing details of the object, i have 1 toolBar at the bottom so i can give some options to user.
When he press on barButtomItem, i want to show a new view but not changing the navigationbar, so if i press back button he goes back from detail view and not from new option.
i know method pushViewController but does not work as i want.
thx in advance!
edit: just to be a more bit more clear if i call
[[self navigationController]pushViewController:loteCompraViewController animated:YES];
i get a new view, related to the new controller but it also changes the navigationbar and that is not good for me.
Give your view controller of the "view showing details of the object" two methods: isShowingOverlayView and dismissOverlayView. In isShowingOverlayView, return YES if you're showing the "new view" because "he press on barButtomItem". In dismissOverlayView, hide the "new view".
Then make your own subclass of UINavigationController. In your subclass, override popViewControllerAnimated: to use those methods of your object-detail view controller, like this:
- (UIViewController *)popViewControllerAnimated:(BOOL)animated
{
if ([self.topViewController respondsToSelector:#selector(isShowingOverlayView)]
&& [(id)self.topViewController isShowingOverlayView])
{
[(id)self.topViewController dismissOverlayView];
return nil;
} else {
return [super popViewControllerAnimated:YES];
}
}
Use this subclass instead of the standard UINavigationController.

Selected index on the "more" view of a UITabbar

How can I manage the user selection on the "more" view of a UITabBar? I have this code to manage the UITabBarItems selections:
- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController {
if (!(viewController == tabBarController.moreNavigationController)) {
int index = tabBarController.selectedIndex;
[[DataManager sharedInstance] setCurrentTabbarIndex:index];
}
}
It works fine for the visible UITabBarItems, but when the user select some item from the "more" view I never get informed about that. Is there some way to catch the user item selection of the "more" view? Thanks!
The "more" view of a UITabBarController is handled separately from the other views. Apple's discussion on this subject says the following:
[The 'moreNavigationController'] property always contains a valid More navigation controller, even if a More button is not displayed on the screen. You can use the value of this property to select the More navigation controller in the tab bar interface or to compare it against the currently selected view controller.
Do not add the object stored in this property to your tab bar interface manually. The More controller is displayed automatically by the tab bar controller as it is needed. You must also not look for the More navigation controller in the array of view controllers stored in the viewControllers property. The tab bar controller does not include the More navigation controller in that array of objects.
Judging from that I would think that you can do something like:
int index = tabBarController.selectedIndex;
if (tabBarController.selectedViewController ==
tabBarController.moreNavigationController) {
index = -1; //assign some placeholder index for the "More" controller
}
A very late answer for a very old question, but here it is anyway, in case someone stumbles upon this.
The solution is to assign yourself as the delegate of the "more" navigation controller. You already have a class that adopts the UITabBarControllerDelegate protocol, so the .h file might look like this:
#interface MyDelegate : NSObject <UITabBarControllerDelegate, UINavigationControllerDelegate>
{
}
Wherever you are assigning your class as delegate, do this:
- (void) assignDelegate:(MyDelegate)myDelegate toTabBarController:(UITabBarController*)tabBarController
{
tabBarController.delegate = myDelegate;
tabBarController.moreNavigationController.delegate = myDelegate;
}
And finally, in your delegate class add this method:
- (void) navigationController:(UINavigationController*)navigationController didShowViewController:(UIViewController*)viewController animated:(BOOL)animated
{
// add code to handle the event
}
Note that none of your delegate methods are invoked when you change tabs programmatically.
The best approach I've found for this problem is to become the delegate for the UITableView which is the topViewController in the UITabBarController.moreViewController.
Implementing the didSelectRowAtIndexPath should solve the problem!
The best (and most secure) implementation for this is described by Stuart Sharpe in his blog: http://initwithstyle.net/2014/02/making-more-of-the-more-view/
Whatever view is being selected will receive viewWillAppear:animated:
Provide this in every view that's controlled by your tab bar, and you can thus extract the identity of the user's selection even when made from a "More" controller.
You can save the tab bar state right in this method, or you can provide your views with a reference back to the tab bar and notify it.

Setting hidesBottomBarWhenPushed leaves bottom bar missing after View Controller is popped

I have the following setup:
A tab bar app.
On one tab there is a navigation controller.
My workflow:
When I push a new viewController onto the navigation controller stack, I set the hidesBottomBarWhenPushed property.
This works great, the tab bar is "pushed" as the new view controller slides in place.
The problem:
When I pop this view controller and the root view controller is once again displayed, however, the tab bar is gone.
The navigation controller has grown to fill the space left by tab bar.
Is there a property I need to set to make the tab bar visible again?
What I have tried:
popping to the root view manually
setting (resetting) the hidesBottomBarWhenPushed for the root view
resizing the root view
resizing the view property of the navigation controller (just leaves a "white space" where the tab bat should be)
What "sorta" worked:
If I set the selected index of the tab bar controller to any other index, the tab bar appears. So I know it is still "around", but this does little to help me.
I had this problem too. I was setting -hidesBottomBarWhenPushed on the wrong view controller.
Wrong (but seems to work until you pop):
self.hidesBottomBarWhenPushed = YES;
[self.navigationController pushViewController:self.anotherViewController animated:animated];
Right:
self.anotherViewController.hidesBottomBarWhenPushed = YES;
[self.navigationController pushViewController:self.anotherViewController animated:animated];
this is the same problem i had, but i got a solution, try this
I found that hiding and then showing the tabbar immediately after the push, solves our problem
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSDictionary *theItem = [items objectAtIndex:indexPath.row];
DetailController *nextController = [[DetailController alloc] initWithItem:theItem];
self.hidesBottomBarWhenPushed = YES;
[self.navigationController pushViewController:nextController animated:YES];
//
//[nextController setHidesBottomBarWhenPushed:YES];
self.hidesBottomBarWhenPushed=NO;
[nextController release];
}
If you're using a UINavigationController and looking for a way to just hide the TabBar (BottomBar) in one view controller, place this code in the view controller you'd like to the hide the TabBar for:
- (BOOL)hidesBottomBarWhenPushed {
return [self.navigationController.visibleViewController isEqual:self];
}
Other approaches I tried with just setting the property caused the TabBar to be hidden after pushing a new view controller from the view controller with the hidden TabBar (even after setting the property to NO).
I do something similar in my app - just calling:
[self.navigationController popToRootViewControllerAnimated:YES];
seems to do the trick and the tab bar is back - admittedly this is in response to a button press rather than the nav bar pop button. I seem to remember it worked fine when using the nav bar back button as well.
Perhaps check you are only setting a single view controller to have the hidesBottomBarWhenPushed property set to YES.
In addition to doing this:
[self.navigationController popToRootViewControllerAnimated:YES];
Initially when you do self.hidesBottomBarWhenPushed = YES;
You have to change for viewControllerToBePushed.hidesBottomBarWhenPushed = YES;
That should do the work!
Curious, I never set this value, but override it on the ViewController I want:
- (BOOL) hidesBottomBarWhenPushed
{
return YES;
}
swift:
self.hidesBottomBarWhenPushed = true
self.performSegueWithIdentifier("viewcontroller_details", sender: nil)
self.hidesBottomBarWhenPushed = false
In the view controller that appears after the one with the toolbar is popped, try this magic:
- (void)viewWillAppear:(BOOL)animated {
[self.navigationController setToolbarHidden:YES animated:YES];
}
Swift 3: From code, you have to set pushedController.hidesBottomBarWhenPushed to true.
Storyboard:
Select the pushed controller, go to Attribute Inspector, select "Hide Bottom Bar on Push" option under View Controller.
I had the same issue and I couldn't fix it with any of the suggested approaches mentioned here.
I came up with a hacky way around this problem and it works fine, although in my case MAYBE because I am working with RxSwift, the issue appears to be a race-condition so I slightly delay the pop action and reveal the tabBar manually, nevertheless it might fix your problem as well:
if self.tabBarController?.tabBar.isHidden == true {
self.tabBarController?.tabBar.isHidden = false
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
self.navigationController?.popViewController(animated: true)
}
} else {
self.navigationController?.popViewController(animated: true)
}