I'm using iOS 5 with Storyboard. My UITabBar is created using the Interface Builder. I have two similar items in my TabBar that is the same list, just with different type of item in it. What I've done, but looks weird to me, is setting a different "Tag" to each UITableView and in the viewDidLoad, and then assign the right type according to the Tag.
- (void)viewDidLoad
{
[super viewDidLoad];
if (self.tableView.tag == 1)
{
type = #"lent";
}
else if (self.tableView.tag == 2)
{
type = #"borrowed";
}
}
Any better way to do it? I'm not creating my UITabBar in code, so my AppDelegate is pretty empty! The type I set is just an attribute in one of my Core Data Entity, on a list I have Borrowed Items and on the other I have Lent Items, but they're the same entity.
You could expose the type as a property on your common view controller, and set it when the relevant tab bar item is selected (tabBarController:didSelectViewController:
from the UITabBarControllerDelegate protocol - your app delegate would be the tab bar controller delegate).
You would set this up as follows. Declare that your app delegate conforms to the UITabBarControllerDelegate protocol, then set it as the tab bar controller's delegate (you have to do this in code since the app delegate is not available to connect to in the storyboard). In your applicationDidFinishLaunching, add the following before you return YES:
UITabBarController *tbc = (UITabBarController*)self.window.rootViewController;
tbc.delegate = self;
Then implement the following delegate method:
- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController
{
switch (tabBarController.tabBar.selectedItem.tag)
{
case 1:
viewController.property = #"propertyA";
break;
case 2:
viewController.property = #"propertyB";
break;
}
NSLog(#"view controller is %#",viewController);
}
You'll need to cast the viewController variable to your actual view controller class, and also assign the relevant tags to the tab bar item of each view controller.
What you have there should work fine. Another option would be to have a common UIViewController super class that has all the functionality and then subclass that base class and provide an implementation of viewDidLoad that sets the appropriate type. Then in Interface Builder you can set the UITabBar view controllers as your appropriate subtypes.
The result would be the same, but it may be a bit more clear in IB what is going on because you don't have to rely on remembering the meaning of each numerical tag.
Related
I created my iOS app with Tab Bar template, so here is UITabBarController with bar buttons. An issue is how to set it as delegate. I found at SO that it has to be set programmatically in AppDelegate, but I believe it's impossible, because I've got no access to Tab Bar Controller as outlet.
I added proper value to #interface (both ViewController and AppDelegate), but doesn't know what to do next.
#interface ViewController : UIViewController <UITabBarControllerDelegate>
I hope I don't have to get rid of whole app template and it's possible to set Tab Bar Controller created in IB to be delegate.
Exactly I want to make it delegate to create on tab select event like this:
- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController;
Any idea?
You can quickly and easily create a new TabBarController Delegate class and connect it as the delegate in the storyboard.
Create a new class :
class TabBarControllerDelegate: NSObject, UITabBarControllerDelegate {
In IB, add an object from the object library to the list of View Controller on the left (note: search "object", it's a yellow cube).
Change the class of this object (your yellow cube in IB) to TabBarControllerDelegate
In IB navigate to your Tab Bar Controller Scene. From the Connection Inspector, drag the delegate circle to the new object you added in Step 3.
Implement your delegate methods in your TabBarControllerDelegate class. Done!
func tabBarController(tabBarController: UITabBarController, shouldSelectViewController viewController: UIViewController)->Bool {
println("Selected a new tab")
}
I don't remember exactly the Xcode's Tab Bar template set up, but in your AppDelegate you can access to your window's rootViewController, cast it to a UITabBarController, and then set its delegate to your AppDelegate or to any other view controller.
Something like this:
UITabBarController *tabBarController =
(UITabBarController *)[[self window] rootViewController];
[tabBarController setDelegate:self]; // In this example your app delegate would implement the UITabBarControllerDelegate protocol.
EDIT
If you want to set your ViewController instance as the delegate:
UITabBarController *tabBarController =
(UITabBarController *)[[self window] rootViewController];
// I assume you have your ViewController instance set as the first view controller of your tab bar controller
// No need for a cast here since objectAtIndex: returns id, but of course you must implement the UITabBarController protocol in your ViewController.
[tabBarController setDelegate:[[tabBarController viewControllers] objectAtIndex:0]]];
EDIT 2
From your ViewController itself you can set the tab bar controller's delegate as rdelmar comments.
Just keep in mind that this cannot be done in the init method because the view controller is not in the tab bar controller yet. The proper place would be viewDidLoad but therefore it will not be executed until the ViewController view loads...
self.tabBarController.delegate = self;
0 lines of code
Drag an Object and subclass it
Xcode > Show File Inspector > Custom Class.
Class: TabBarControllerDelegate.
Set delegate to that Object
Put your existing code in that Object
This is the code you already have in your current UITabBarControllerDelegate.
class TabBarControllerDelegate: NSObject, UITabBarControllerDelegate {
// Delegate code goes here
}
What about create a viewController lets say MyTabController subclass UITabBarController
#interface MyTabController : UITabBarController<UITabBarControllerDelegate>
and set the tab Controller's class in you storyboard to MyTabController instead of UITabBarController, then put self.delegate = self; in your viewDidLoad
implement:
- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController;
and here you are.
Edit:
If you find self.delegate = self; is odd, which it is, you can create an outlet in your MyTabController
IBOutlet UITabBarController *tabBarController; and connect it to the tab controller in your storyboard.
Then you can put tabBarController.delegate = self;
Let me give an example what about what I want to do:
I have a tab that has 4 TabBarItems, so it contains 4 controllers. And there is a controller that must not get shown up in tab bar's icons, but it will get shown when a button inside one of these 4 controllers are touched. So when it get's shown, the tabbar must not have any selected tabs, every item must be deselected. It must be like a ghost controller that is not shown in tab icons but it's actually a controller that's in tab bar controller.
What's the best way to achieve this? Fyi, I don't want a modal dialog, the tabbar must always be visible underneath.
One thing which might help is knowing when a transition between tabs is happening. If you display your ghost view as soon as the transition happens, you might have enough control to do what you want.
Here's how I do that: first, sub-class UITabBarController to TabViewController (for example). In your TabViewController, include this method:
// Pass this message on to views so they know when transitions are occuring
- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController {
UIViewController <UITabBarControllerDelegate> *obj;
for ( obj in self.viewControllers ) {
if ( [obj respondsToSelector:_cmd] ) {
[obj tabBarController:tabBarController didSelectViewController:viewController];
}
}
}
Set each of your tabbed view classes to adopt the UITabBarControllerDelegate protocol. Then, include a method like this in each class to "intercept" the transition events:
// This is called when a transition between tabs happens
- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController {
if ( [viewController isEqual:self] ) {
// Transitioning to me
// Do stuff
}
else {
// Transitioning to someone else
// Do stuff
}
}
(note: there might be cleaner ways of implementing this, but it's been working fine for me...)
My Tabview controller has a tableview appearing as the first and initial view. The view controller class is a subclass of uitableviewcontroller, so I am not using XIB for this.
Since I am giving the view name directly in the interface builder for the MainWindow of the tab view, I am not sure where I can set the style of the tableview, which appears in 'plain' style at the moment. I want to change it to 'grouped', however there is no code where I call and allocate the tableview controller (in which case I would have used the initWithStyle).
Any ideas on how I can change this initial tableview to grouped style instead of plain style?
I also tried overriding the initWithStyle and set it like below, but does not work.
- (id)initWithStyle:(UITableViewStyle)style {
// Override initWithStyle: if you create the controller programmatically and want to perform customization that is not appropriate for viewDidLoad.
self = [super initWithStyle:UITableViewStyleGrouped];
if (self) {
// Custom initialization.
}
return self;
}
Thanks,
Try the following then:
UITableView* table = [[UITableView alloc]initWithFrame:myFrame style:UITableViewStyleGrouped];
Replace UITableView by the name of your custom class
you can choose the tableview from the .xib and set the style to Grouped
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.
Trying to mimic/copy the built-in address book, specifically the behavior when editing a contact or viewing an existing contact's Info from inside the Phone app. When you navigate to another tab, the editing state is reset and the "New Contact" or "Info" view is popped so that when you come back to the Contacts tab, you are back at the root table view.
I have most of this working inside viewWillDisappear using setEditing: and popToViewController: however I get strange behavior when the user navigates from the Info view to the table view using the back button. Even if I pop to the root table view controller, it seems to be using the default UITableViewController class and not my subclass (e.g. standard selection behaviors instead of my overrides to push the detail view.)
Any hints? IPD
Here's some code to illustrate:
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
// This is to clean up from the colored bar in detail view
self.navigationController.navigationBar.tintColor = nil;
// These are to match the behaviour of Contacts app
[self setEditing:NO animated:NO];
// This is the tricky part: works when switching tabs, but not when back button was going to pop anyway!!
[self.navigationController popToViewController:rootViewControllerForTab animated:NO];
}
The -viewWillDisappear: method is not the best place for modifying the view controller stack for your navigationController because it is triggered both when you switch tabs and when a view is pushed on top of it.
I played around with this a bit and found that the best place for this is in the -[UITabBarControllerDelegate tabBarController:didSelectViewController:] method. So, first you need to designate an object to be the delegate for your tab bar (I used the app delegate). Bind the delegate property of your UITabBarController to an object implementing the UITabBarControllerDelegate protocol in code or in Interface Builder.
Then, implement the -tabBarController:didSelectViewController: method. The trick now is how to tell when your "address book" tab is being switched to. I kept track of the view controller for the tab in question using a property of type UINavigationController (the root view controller for the tab). After binding the tab1NavController property to the actual instance using Interface Builder, it can be used to compare to the viewController parameter to see what tab was just selected.
#interface Pop2RootTabSwitchAppDelegate : NSObject
<UIApplicationDelegate, UITabBarControllerDelegate> {
UINavigationController *tab1NavController;
}
#property (nonatomic, retain) IBOutlet UINavigationController *tab1NavController;
#end
#implementation Pop2RootTabSwitchAppDelegate
- (void)tabBarController:(UITabBarController *)tabBarController
didSelectViewController:(UIViewController *)viewController {
NSLog(#"[%# tabBarController:%# didSelectViewController:%#]", [self class],
tabBarController, viewController);
if (viewController == tab1NavController) {
NSLog(#"viewController == tab1NavController");
[tab1NavController popToRootViewControllerAnimated:NO];
}
}