changing tabbar images when tabbar not root view - ios

I am looking at changing the graphics on a tabbar, such as
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
UITabBarController *tabBarController = (UITabBarController *)self.window.rootViewController;
UITabBar *tabBar = tabBarController.tabBar;
UITabBarItem *tabBarItem1 = [tabBar.items objectAtIndex:0];
UITabBarItem *tabBarItem2 = [tabBar.items objectAtIndex:1];
UITabBarItem *tabBarItem3 = [tabBar.items objectAtIndex:2];
UITabBarItem *tabBarItem4 = [tabBar.items objectAtIndex:3];
tabBarItem1.title = #"Home";
tabBarItem2.title = #"Maps";
tabBarItem3.title = #"My Plan";
tabBarItem4.title = #"Settings";
The problem I have here is that my tabbarcontroller is not my root view, so how can I reference the tabbarcontroller to change the tab images?
I am following a suggestion from this post (Can I have more than 1 UITabBarController?) that refers to having a tableview that links to one or more tabbarcontrollers.
So my root view is not a tab bar, but the tab bar view is loaded after coming from one previous screen.
I have this all working, with the initial screen and then the tab bar, and everything is working fine, I just need to change the graphics on the tab bar, and am not able to do this as all tutorials on changing tab bar graphics use the app delegate and refer to the tabbarcontroller as the root view.
Any help on this greatly appreciated!

Tab bar items belong to the individual content view controllers in each tab, so rather than trying to reference the tab bar controller, you should change the tab item properties in those controllers. If you want those changes to be visible as soon as the tab bar controller comes on screen, you need to put those customizations in their awakeFromNib or initWithCoder methods (for controllers set up in IB).

Richard,
Firstly, the post you reference pointed out that this is very bad visual design, but anyway, if you need to do it.
How you reference the tab bar controller depends on how you instantiate it. This stuff should be fairly obvious, so I'm guessing that you're working with xibs or storyboards.
Either way, when you load the second view that is going to have the tab bar on it, you'll be loading a view controller to control the view. Just bind the tab bar to a property of the view controller in IB, and you can do your set up in the view did load of the view controller.
Actually, if you're using IB ( XIB or storyboards), you can just set up your icons there.

Related

How to make Navigation Bar and Tab bar always visible on every screen even after implementing PushView controller

I have a iPhone app in which need to add UISearchbar on navigation bar, so that user can search from any where inside the app. When User Searches it displays search result in a Tableview controller and User can select any row which pushes to other controller.
Below is the Structure which i have in StoryBoard
Navigation Controller
Tab Bar Controller
View Controller1
View Controller2
View Controller3
View Controller4
I can successfully add the UIsearchbar on Navigationbar in ViewController 1
UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:SEARCH]];
imageView.frame = CGRectMake(0, 0, 20, 20);
UITapGestureRecognizer *tapGesture =
[[UITapGestureRecognizer alloc] initWithTarget:self
action:#selector(searchTapped:)];
[imageView addGestureRecognizer:tapGesture];
self.searchButton = [[UIBarButtonItem alloc] initWithCustomView:imageView];
self.searchButton.style = UIBarButtonItemStylePlain;
self.searchButton.target = self;
[self.searchButton setTintColor:[UIColor whiteColor]];
self.tabBarController.navigationItem.rightBarButtonItem = self.searchButton;
I can successfully implement the Search results as well in Tableview controller but when user select row it Pushes to different controller. Below is the code which implmenetd to push to different controller.
UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:#"Main" bundle: nil];
EntityViewController *entityController = [mainStoryboard instantiateViewControllerWithIdentifier:ENTITYIDENTIFIER];
entityController.dataDic = result;
[self.navigationController pushViewController:entityController animated:YES];
The problem here is when it pushes to different controller the TabBar is completely hidden and Navigation bar right button is completely Hidden.
I tried to Unhide the tab bar and Navigation bar but no effect. I tried all possibilities but nothing worked.
Can some one please let me know what is right approach to solve this issue.
Thanks In Advance.
Have you tried switching the order of the UINavigationController and the UITabController?
Right now, it seems like you have a UINavigationController with a UITabController "inside" it. So, when you push something else to the UINavigationController, the UITabController is being pushed aside, as it is no longer relevant.
If you were to have the UITabController on the top most level, with the UINavigationController at each of the tabs (or only on the relevant ones), when you'll push another view controller to that UINavigationController, it won't affect the tab bar, since it will still be on the top most level, completely unaffected by the push.
I hope this helps. Good Luck!
Thanks for the help. I resolved the issue by making Tab Bar controller as TOP View and Navigation controller for each view ( as per requirement. I made SearchController separately so that user can search from any where inside the app.
I updated this as it can be help full for those who needs to implement similar kind of requirement.
If there is a better approach of implementation please do provide your comments.
Thanks

How to link a tabBarItem to a viewController programmatically (iPhone, iOS)

I have an application with a login system. I want the tabBarController to change dynamically at runtime if the user login himself successfully! I have 5 tabs (Accueil, Tous les Voyants, Inscription, Connexion, Aide).
When the user hit the login button, I want to change Inscription to Achat Jetons and Connexion to Profile and link another ViewController to both of theses tabBarItems!
Right now, I've successfully managed to replace the title and image logo of my tab bar. But I don't know how to link the viewControllers to them! Here's what I got right now:
- (IBAction)BTN_ConnexionClick:(id)sender {
UITabBarController *tabBarController = (UITabBarController *)self.tabBarController;
UITabBar *tabBar = tabBarController.tabBar;
UITabBarItem *tabBarItem1 = [tabBar.items objectAtIndex:2];
UITabBarItem *tabBarItem2 = [tabBar.items objectAtIndex:3];
[tabBarItem1 setFinishedSelectedImage:[UIImage imageNamed:#"menu_iOS_achat.png"] withFinishedUnselectedImage:[UIImage imageNamed:#"menu_iOS_achat.png"]];
[tabBarItem2 setFinishedSelectedImage:[UIImage imageNamed:#"menu_iOS_profile.png"] withFinishedUnselectedImage:[UIImage imageNamed:#"menu_iOS_profile.png"]];
tabBarItem1.title = #"Achat Jetons";
tabBarItem2.title = #"Profile";
}
I have created 2 new viewControllers via StoryBoard IB, I just don't know how to replace old linked viewController with new ones! Thanks for your help! :)
The mistake you're making is that you're changing the tab bar controller's tab bar's tab bar items directly. Don't! Change the tab bar controller's view controllers. The tab bar controller gets its tab bar items from them.
You might want to read my book on this topic:
http://www.apeth.com/iOSBook/ch19.html#_configuring_a_tab_bar_controller
Notice especially:
The tab bar controller’s tab bar will automatically display the tabBarItem of each child view controller
Do not do anything to mess that up! (You are messing it up.) Manipulate a view controller's tabBarItem. Manipulate a tab bar controller's viewControllers. Do not touch a tab bar controller's tab bar yourself.

iOS Right navigation button does not display in UINavigationBar

I have following view controllers hierarchy:
View Controller VC1
Tab Bar Controller TBC1 - configured in storyboard to lead to a Table View Controller TVC1 and a Map View Controller MVC1
Table View Controller TVC1
Table View Controller TVC2
In VC1, I do this:
[self.navigationController pushViewController:TBC1 animated:YES];
This rightly brings up tab bar controller, with TVC1 in focus.
TVC1 shows back button in its navigation bar (programmatically created from VC1 code), which will get me to VC1, which is expected.
However, from TVC1 onwards, I need one more navigation to TVC2. I am trying to add right button to the TVC1 navigation bar for this, but it doesn't show up.
Here is the code I use in TVC1 (rightButton is UIButton type property of TVC1):
self.rightButton = [[UIBarButtonItem alloc]
initWithBarButtonSystemItem: UIBarButtonSystemItemAdd
target: self
action: #selector(MySelector:)];
self.rightButton.style = UIBarButtonItemStyleBordered;
self.rightButton.title = #"";
self.navigationItem.rightBarButtonItems = [NSArray arrayWithObjects: self.rightButton, nil];
(specified blank title and style just to ensure if that's the issue which is causing this, I don't actually need those values)
MySelector is declared in TVC1.h as:
- (void) MySelector:(id)sender;
And it is properly implemented, too.
But rightButton above does not display in TVC1 navigation bar.
What am I missing?
I suspect its with TBC1 (tab bar) that comes between VC1 and TVC1, and somehow it resets navigation properties.
But then I argue that I see navigation bar on TVC1, and a left button leading to VC1.
I checked that in TBC1, self.navigationItem.rightBarButtonItems has 1 object inside which is definitely the rightButton I am adding.
Where am I wrong?
Note: Above is found in all of iOS 5.0, 5.1 and 6.0 simulators.
It seems to me that your are missing UINavigationController between TVC1 and TVC2 in your storyboard. If you are using storyboards then you can create navigation item Add button type on the navigation controller itself and have a PUSH segue to TVC2. See this diagram if that makes sense. If this doesn't solve your problem then please upload example code and I will have a look.
[EDIT]
I had reproduced your issue by creating your view controllers structure in storyboard.
If you notice here TVC1 doesn't have it's UINavigationController but it is inheriting it from VC1. Solution to your problem is rather than adding rightButton onto self add it to self.parentViewController and you will see rightButton in TVC1. But mind you it will also appear in MVC1 as it is belong to TBC1's parent. You can hide right bar button in MVC1's viewWillAppear if you don't want it there. Following is the code.
self.rightButton = [[UIBarButtonItem alloc]
initWithBarButtonSystemItem: UIBarButtonSystemItemAdd
target: self
action: #selector(MySelector:)];
self.rightButton.style = UIBarButtonItemStyleBordered;
self.parentViewController.navigationItem.rightBarButtonItems = [NSArray arrayWithObjects: self.rightButton,nil];
If you want to Add right button into the TVC1's navigation controller then you need Embed TVC1 into UINavigationController. To do this, select TVC1 screen in the storyboard -> Editor -> Embed In->Navigation Controller. When you do this your code will also work and will show you right button but you will have two navigation controllers(see image below) in it because of your structure of storyboard. You will need to hide Parent's navigation controller in to the TVC1's view did load and have left button to Pop to Parentview Controller. You do the same in MVC1.
Hope this helps! Happy coding :)
Had the same problem recently in Swift and found that embedding the child view in a navigationController was still the correct way to be able to access the rightBarButtonItems.

Change Image of UItabbar Item, Using storyboards

I have the following story board:
As you can see there is a tab bar application with 5 tabs, on the storyboard I've assign the logo for each tab. Now when the user clicks a cell in a particular view I want to change the image of one of the tabs. How can I do this? I don't have an instance of the tab bar view controller or items since storyboards pretty much does all this for me. So my question is what methods do I have to implement to change the image? If I need the tab bar controller how can I get its instance and in which class should I point it to?
Thank you very much,
In any UIViewController class that is part of the Tab Bar hierarchy, all you have to do to get in instance of the tab bar controller is:
//In UIViewController
UITabBarController *tabBarController = self.tabBarController;
You can then change the image as so
//Suppose you want to change the 1st (0th) tab bar image
UITabBarItem * tabItem = [tabBarController.tabBar.items objectAtIndex: 0];
tabItem.image = //whatever image you want to change to
Each UIViewController has a property called tabBarItem which is a UITabBarItem that the tab bar controller uses to set the image representing that controller. You can manipulate that to change the image of the controller in question.
I found that -- at least in Xcode 6.1.1 using Swift -- the direct manipulation of the tabBarItem did not work for me.
However, #borrrden's answer put me on the right track. Apple's documentation for UITabBarController states pretty clearly:
You should never access the tab bar view of a tab bar controller
directly. To configure the tabs of a tab bar controller, you assign
the view controllers that provide the root view for each tab to the
viewControllers property.
...
Tab bar items are configured through their corresponding view
controller. To associate a tab bar item with a view controller, create
a new instance of the UITabBarItem class, configure it appropriately
for the view controller, and assign it to the view controller’s
tabBarItem property.
Therefore, in accordance with that, below is what I came up with that did work for me.
It's written in Swift, and I hope that future readers can translate it accordingly if they need to (I also changed the image names to be super-generic).
I also used UIImage's imageWithRenderingMode method so I could use custom images instead of the shadowy silhouette default images that iOS creates (I would like to credit #NSHeffalump's answer here for that...).
if let viewControllers = tabBarController.viewControllers as? Array<UIViewController> {
var tabBarItemImageNames = ["TabBarItemImage0","TabBarItemImage1","TabBarItemImage2","TabBarItemImage3","TabBarItemImage4"]
var vcIndex = 0
for vc:UIViewController in viewControllers {
let selectedImage = UIImage(named: tabBarItemImageNames[vcIndex])?.imageWithRenderingMode(UIImageRenderingMode.AlwaysOriginal)
let image = UIImage(named: tabBarItemImageNames[vcIndex])?.imageWithRenderingMode(UIImageRenderingMode.AlwaysOriginal)
var tabBarItem = UITabBarItem(title: "", image: image, selectedImage: selectedImage)
vc.tabBarItem = tabBarItem
vcIndex++
}
}

UINavigationController has extra status bar gap at top

This looked simple enough when I set it up, but I can't explain why this gap is present between the status bar and the navigation bar. Also, the contained view looks like it may be properly aligned, and it's just the nav bar that is shifted down. The gap looks like the size of the status bar, so I expect that has something to do with it, but I don't know what.
Here is the code for setting up the navigation controller:
- (void)viewDidLoad
{
[super viewDidLoad];
advancedVC = [[AdvancedSearchFormVC alloc] initWithNibName:#"AdvancedSearchForm" bundle:nil];
UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:advancedVC];
nav.navigationBar.tintColor = [UIColor defaultNavBarTint];
nav.navigationBar.topItem.title = NSLocalizedString(#"SearchTitle", nil);
UIBarButtonItem *searchButton = [[UIBarButtonItem alloc] initWithTitle:NSLocalizedString(#"SearchButton", nil) style:UIBarButtonItemStylePlain target:self action:#selector(refreshPropertyList:)];
nav.navigationBar.topItem.rightBarButtonItem = searchButton;
self.view = nav.view;
}
The rootViewController uses a view from a xib file, where I have simulated the status bar, the navigation bar, and the tab bar.
The problem is indeed that the navigation controller always expects to leave room for the status bar, which is the 20 pixel gap. I searched around for a while before I found this solution which works:
//nav is assumed to be a subclass or instance of UINavigationController
nav.view.frame = CGRectOffset(nav.view.frame, 0.0, -20.0);
//you can then add the navigation's view as a subview to something else
I originally found an answer which did this offset to the navigationbar's view, but it didn't work. It works when you do it to the navigation controllers actual view.
I use this technique to add a navigation controller from another nib to an empty view of my main nib, so I can position it anywhere within the main screen as a subview. By using an empty view as a placeholder and positioning frame on my main nib, I create a separate nib and class to manage the navigation, which manages other nibs used to handle their screens. This way I can solve the classic "how do I add a banner, image, or custom views above my navigation controller" while having a navigation controller as a subview...in iOS 5 to be specific.
It's also worth mentioning that I use the app delegate to store and access all the other controllers, so they are retained by a persistant instance which I can access from any class. Create and synthesise some properties in the app delegate of all your controllers, and in viewDidLoad create instances. That way I can reference all the controllers used in my app later anywhere by adding:
//this shows how to store your navigation controllers in the app delegate
//assumes you've added 2 properties (UINavigationController*)"navController" and (UIViewController*)"rootController" in your app delegate
//...don't forget to add #import "AppDelegate.h" to the top of the file
AppDelegate *app = (AppDelegate*)[[UIApplication sharedApplication] delegate];
[app.navController pushViewController: app.rootController animated:YES];
//now apply the offset trick to remove the status gap
app.navController.view.frame = CGRectOffset(app.navController.view.frame, 0.0, -20.0);
I had the same problem before. The code I used to add UINavigationBar to UIViewController:
UINavigationController *nc = [[UINavigationController alloc] initWithRootViewController:self];
[self.view addSubview:nc.view];
Solution:
Check the box "Wants Full Screen" with Attributes inspector of your UIViewController.
You can try to set the attribute Under Top Bars unchecked from Attributes section of UIViewController.
As we all know by now, the 20 pixel shift is to provide space for the Status bar on the top.
But infact, the view controllers coordinate system is kept in place and only the navigation bar frame is shifted down by 20 pixels. This makes the navigation bar to actually overlap the top 20 pixels of the view.
Logging the navigation bars frame origin, it will show (0.0, 20.0)
So the solution is to simply reposition the navigation bar's origin to (0.0, 0.0) in ViewWillAppear.
self.navigationController.navigationBar.frame = CGRectMake(0.0, 0.0, self.navigationController.navigationBar.frame.size.width, self.navigationController.navigationBar.frame.size.height);
Since you're adding advancedVC as a subview of self.view, it is being added inside the frame of self.view which I'm guessing is already compensating for the status bar.
You can probably easily fix this by adding this line:
nav.view.frame = self.view.frame;
Just above this line:
self.view = nav.view;
-
Other Thoughts
I'm not privy to your entire setup, but self.view may not be needed at all. Simply make your advancedVC instance the rootViewController of the UIWindow instance contained in your App Delegate.
The problem was resolved by fixing the way the navigation controller was inserted. Instead of inserting it into a view that had been put onto the tabbar controller, the navigaiton controller should have been put directly onto the navigation controller.
advancedSearchFormVC = [[AdvancedSearchFormVC alloc] initWithNibName:#"AdvancedSearchForm" bundle:nil];
UINavigationController *searchNavController = [[UINavigationController alloc] initWithRootViewController:advancedSearchFormVC];
This is just one controller that is on the tabbar controller, replacing the advancedSearchFormVC at the same time. Then this nav controller was added to the array of controllers that got put onto the tabbar controller.
Sorry for any trouble, but this was one of those problems I can look directly at and not see it. I should have seen this earlier, because I had another nav controller already on the tabbar controller, and it was set up the same way.
Thanks for your assistance.
The problem is that UINavigationController.view should be added to the top view.
Just find the top one and it will be working great.

Resources