Subclass UITabBarController in iOS6 with storyboards - ios

I started a new project with iOS 6 ARC and Storyboards
I made a very simple app that has a Tabbar and 2 views
so now I created a TestViewController file with is extending UITabBarController and I put the custom class in the storyboard.
now the issue is that I am trying to implement some delegate methods like
- (BOOL) tabBarController:(UITabBarController *)tabBarController shouldSelectViewController:(UIViewController *)viewController;
But it never calls it. Can anyone help?
I noticed too that if I put that code in the first view controller it works fine. It seems like the first view controller overwrites the TabBarController before. I am very confused.
for more testing I added in the TestViewController.m some logging:
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
NSLog(#"%#",self.tabBarController.viewControllers);
}
and the view controllers are null.
I even tried doing self.tabBarController.delegate = self;
But still no solution.

TestViewController is your tab bar controller, so your log should just be self.viewControllers, not self.tabBarController.viewControllers. You shouldn't need to add the property viewControllers either, since your subclass inherits that property from UITabBarController. The reason your delegate message is not called is because of the same problem. You should set the delegate with (in TestViewController):
self.delegate = self;
This is assuming that you want TestViewController to be the delegate, it wasn't clear to me if that's what you wanted.

Have you tried linking them in Interface Builder?
Add this to your header file first though
#property (nonatomic, retain) IBOutlet UITabBarController *tabBarController;

Related

How to segue back from one view controller to another within a custom UIView?

I want to segue back from ViewControllerTwo to ViewControllerOne. I created a button that is responsible for doing that, but my problem is that the button is part of custom UIView class that is added to ViewControllerTwo, the button is not a part of the main view of ViewControllerTwo.
So in the custom UIView class I have the method that reacts if the button is clicked...
-(void)buttonClicked{
[SecondViewController performSegueWithIdentifier: "ShowFirstViewController" sender:nil];
}
When I do this I get an error: "performSegueWithIdentifier not a method of class" which makes sense.
So how can I segue between two viewcontrollers where the button responsible for the segue is not actually part of either view controller and is in a different class.
I think you can have a delegate call back to your SecondViewController and implement the performSegueWithIdentifier in the delegate callback method in SecondViewController.
It goes like this:
Above your custom UIView class interface create a protocol like this
#protocol CustomViewDelegate <NSObject>
- (void)buttonDidTap;
#end
Then create a property in your interface
#property (nonatomic, weak) id <CustomViewDelegate> delegate;
In your custom UIView *.m add this
-(void)buttonClicked{
[self.delegate buttonDidTap];
}
Conform the protocol to your SecondViewController like this
#interface SecondViewController: UIViewController <CustomViewDelegate>
set the delegate in your viewDidLoadMethod like this
-(void)viewDidLoad{
[super viewDidLoad];
self.yourCustomView.delegate = self;
}
implement this method inside the view controller .m file
- (void)buttonDidTap{
[self.performSegueWithIdentifier: "ShowFirstViewController" sender:self];
}
I'm more of a swift guy i think this should work fine.
iOS 9.3, Xcode 7.3, ARC enabled
This is what I'd do to troubleshoot:
Step 1: Make sure that you have a proper storyboard identifier for the view controllers you wish to segue between. The views simply attach to the view controllers, custom or not.
To do this, go to "*.storyboard" show the Utilities (right pane) and navigate to the Identity Inspector. Make sure you have "ShowFirstViewController" entered in the Storyboard ID field.

Adding an App Delegate to multiple View Controllers with reusable code

Edit: I've since learned that what I was doing is very, very poor design. Do not do what I did. Instead, consider reading up on how MVC works, and making proper classes. Do not substitute controllers for objects.
Not sure if I'm going about my architecture the proper way, but what I'm doing is declaring an app delegate in each of my view controllers, and then giving the app delegate it's own view controller delegate for each view controller.
I end up adding the same three lines to each view controller, and another view controller to the app delegate each time I create a new view controller. Is there an easier way to do this with inheritance (I.E. give all view controllers an appDelegate property, then assign it in some parent function).
Here's what I'm currently doing:
//AppDelegate.h
#interface AppDelegate
#property (strong, nonatomic) MyViewController* myViewDelegate;
#property (strong, nonatomic) MySecondViewController* mySecondViewDelegate;
#end
//MyViewController.m
#interface MyViewController ()
#property (strong,nonatomic) AppDelegate* appDelegate;
#end
#implementation MyViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.appDelegate = (AppDelegate*) [UIApplication sharedApplication].delegate;
self.appDelegate.myViewDelegate = self; //MySecondViewController would use:
//self.appDelegate.mySecondViewDelegate = self;
}
...
So two things:
I don't really see a way around declaring new view controllers in the app delegate without using a single root controller that gets assigned on segues, but is having multiple view controller delegates like I'm considered poor design in the first place?
I need a way for ViewDidLoad to call a parent ViewDidLoad to set the value of the app delegate property, as well as the declaration for that app delegate.

How to change a UILabel one one View Controller from another View Controller?

I am relatively new to Xcode and have tried to find the answer by searching, without luck.
My app has 5 View Controllers, V1 through V5, which are embedded in one Tab Bar Controller. Each View Controller has a segue to one and the same Setup Menu View Controller. The Menu changes some labels on the View Controllers. I use a delegate to make sure that the View Controller that calls the Menu gets updated with the new settings when you leave the Menu. However, this allows me to modify only the labels on the View Controller that called the Menu Controller, not on the 4 other ones.
I work form a Story Board. Is there a simple way to set the UILabels on V2, V3, V4 and V5 from V1 (and vice versa), or even better, set the labels on V1 through V5 from the Menu View Controller (which is not embedded in the Tab Bar Controller)?
I have seen something that could help here, but this seems rather complicated for what I want. The label changes I need are quite simple and are all predefined. Is there a method that is called every time you switch tabs in a tabbed application? Similar to ViewDidLoad?
This sounds like a good time for NSNotificationCenter. You are going to have your MenuViewController generate a notification with the new data that should be updated in your other view controllers:
// User has updated Menu values
[[NSNotificationCenter defaultCenter] postNotificationName:#"MenuDataDidChangeStuffForLabels" object:self userInfo:#{#"newLabelValue" : labelText}];
In your V1, V2, etc. you can add subscribe to these notifications using this code in your viewDidLoad method:
- (void)viewDidLoad {
[super viewDidLoad];
// Subscribe to NSNotifications named "MenuDataDidChangeStuffForLabels"
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(updateLabelText) name:#"MenuDataDidChangeStuffForLabels" object:nil];
}
Any object that subscribes using that code will call the updateLabelText method anytime a notification with that name is posted by the MenuViewController. From that method you can get the new label value and assign it to your label.
- (void)updateLabelText:(NSNotification *)notification {
NSString *newText = notification.userInfo[#"newLabelValue"];
myLabel.text = newText;
}
What I would do is subclass the tab bar controller and set that as the delegate for the menu view controller. From there, you can get updated when the labels are supposed to change and then communicate with the 5 tabs and update the labels.
Alternatively, you could use NSNotifications to let all the 5 view controllers know when settings change.
Lastly, you could add the menu settings to a singleton and have all of the view controllers observe the various properties that can change.
The label changes I need are quite simple and are all predefined. Is there a method that is called every time you switch tabs in a tabbed application? Similar to ViewDidLoad?
Regarding this question, the methods you're looking for are viewWillAppear: and viewDidAppear.
Here is a very simple solution if your workflow is also simple. This method changes all the labels from the different ViewControllers directly from what you call the Menu ViewController.
Let's say you have the following situation :
The blue ViewController is of the FirstViewController class. The green ViewController is of the SecondViewController class. The labels on each of those are referenced by the properties firstVCLabel and secondVCLabel (on the appropriate class' header file). Both these ViewControllers have a "Modal" button which simply segues modally on touch up inside.
So when you clic on any of these two buttons, the orange ViewController (of ModalViewController class) is presented. This ViewController has two buttons, "Change Label" and "Back", which are linked to touch up inside IBActions called changeLabel: and back:.
Here is the code for the ModalViewController :
#import "ModalViewController.h"
#import "FirstViewController.h"
#import "SecondViewController.h"
#interface ModalViewController ()
#end
#implementation ModalViewController
// Action linked to the "Change Label" button
- (IBAction)changeLabel:(id)sender {
// Access the presenting ViewController, which is directly the TabBarController in this particular case
// The cast is simply to get rid of the warning
UITabBarController *tabBarController = (UITabBarController*)self.presentingViewController;
// Go through all the ViewControllers presented by the TabBarController
for (UIViewController *viewController in tabBarController.viewControllers) {
// You can handle each ViewController separately by looking at its class
if ([viewController isKindOfClass:[FirstViewController class]]) {
// Cast the ViewController to access its properties
FirstViewController *firstVC = (FirstViewController*)viewController;
// Update the label
firstVC.firstVCLabel.text = #"Updated first VC label from Modal";
} else if ([viewController isKindOfClass:[SecondViewController class]]) {
SecondViewController *secondVC = (SecondViewController*)viewController;
secondVC.secondVCLabel.text = #"Updated second VC label from Modal";
}
}
}
// Action linked to the "Back" button
- (IBAction)back:(id)sender {
[self.presentingViewController dismissViewControllerAnimated:YES completion:nil];
}
For the sake of completeness, here are FirstViewController.h :
#import <UIKit/UIKit.h>
#interface FirstViewController : UIViewController
#property (weak, nonatomic) IBOutlet UILabel *firstVCLabel;
#end
And SecondViewController.h :
#import <UIKit/UIKit.h>
#interface SecondViewController : UIViewController
#property (weak, nonatomic) IBOutlet UILabel *secondVCLabel;
#end
There is no relevant code in the implementation of these classes.
Thanks a lot guys, I am impressed by your quick responses. In this particular case, viewWillAppear does the trick:
- (void)viewWillAppear:(BOOL)animated
{ [self AdaptLabels];
NSLog(#"View will appear.");
}
Every time a new tab is chosen, it updates the labels in the new View, according to a global variable set by the Menu, just before they appear. Very quick and clean. Thanks to all of you!

Checking when a TabBar item is clicked - Objective C

I have a ViewController with 2 sub containers. The first sub container points to a ViewController with a TabBar inside it. The second sub container is a ViewController that contains a collection view. Now my issue is trying to access the first sub containers TabBar so that when an Item is clicked, I can know which item is clicked and process my data.
The main ViewController has a class. All the other sub containers for that ViewController also have a class. Here is the .h for my sub container with the Tab Bar:
#import <UIKit/UIKit.h>
#interface home_tab : UIViewController <UITabBarControllerDelegate>{
}
#end
.m:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
-(void) tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController
{
NSLog(#"working");
}
Now when clicking on the Tab Bar that is populated, didSelectViewController is never called.
I am using storyboard.
Suggestions and thoughts?
Try this on your viewDidLoad
- (void)viewDidLoad
{
[super viewDidLoad];
[[self tabBarController]setDelegate:self];
// Do any additional setup after loading the view, typically from a nib.
}
It's just an suggestion :)
[[self tabBarController]selectedIndex] This will return the index of the selected tab.
I think you have a couple problems atleast from what I can see here,
You are using a TabBar inside of a ViewController, not a UITabBarController, thus you need to use UITabBarDelegate not UITabBarControllerDelegate. You will have to manage the view controllers or whatever view you will want to be loaded on your own most likely with the delegate callback:
-(void)tabBar:(UITabBar *)tabBar didSelectItem:(UITabBarItem *)item;
Also you dont have a UITabBar defined in your controller, therefore your ViewController has no idea you have a UITabBar in your Storyboard. You need something like this:
#interface ViewController : UIViewController <UITabBarDelegate>
#property (weak, nonatomic) IBOutlet UITabBar *tabBar;
#end
Then you will need to Control drag from the ViewController to your UITabBar and back to connect the Delegate in your Storyboard.
Id recommend using a UITabBarController so you dont have to manage the views yourself depending on what you are trying to accomplish.
Hope this helps!
the didSelectViewController method is part of the UITabBarControllerDelegate Protocol and is called on the UITabBarController's delegate. Did you set the delegate of the tab bar controller to the current instance of your subcontainer? Inside ViewDidLoad: do something like this:
[self.tabBarController setDelegate:self];
You can also set a delegate on the UITabBar rather than the controller, and the UITabBarDelegate Protocol contains a method tabBar:didSelectItem: that would be called.

didSelectViewController method not being called (with storyboard)

I have 2 versions of a tabbed ios5 application, one created using a storyboard and one using xib files. The storyboard version does not call the UITabBarControllerDelegate method didSelectViewController (the xib version does). Something is (I think) missing from the storyboard, but I don't know what. Another way of framing the question might be — how can I refer to the UITabBarController object instantiated by the storyboard?
Thanks for your help.
EDIT: The tab bar controller delegate is set:
In AppDelegate.h:
#interface MyAppDelegate : UIResponder <UIApplicationDelegate, UITabBarControllerDelegate>
#property (strong, nonatomic) UITabBarController *tabBarController;
In AppDelegate.m:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.tabBarController.delegate = self;
return YES;
}
Then later in AppDelegate.m, the delegate method is:
- (void) tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController
{
NSLog(#"Got Here");
}
The NSLog output never appears. The problem seems to me to be that I am not correctly referencing the tab bar controller object which has been instantiated by the storyboard.
How do I do that?
I had this issue.
If you're not using storyboards, setting the UITabBarController delegate in the AppDelegate is the way to go. However, with Storyboards, the AppDelegate has no idea where the tabBarController is on startup. You'd think by subclassing the tabBarController and adding the delegate method:
(void)tabBarController:(UITabBarController *)tabBarController
didSelectViewController:(UIViewController *)viewController {
}
... would be enough. But, it's irritatingly not.
I needed to know when a user had pressed a tab button. I needed to know this more that I needed to know that the viewController's "- (void)viewWillDisappear:(BOOL)animated {}
" method had been run.
I decided to make my UITabBarController a delegate of itself. This seemed silly to me but I did the following...
#import <UIKit/UIKit.h>
#interface PlumbsTabBarController : UITabBarController <UITabBarControllerDelegate>
#end
And then, in my viewDidLoad method, wrote the following:
[self setDelegate:self];
Which enabled my tab bar delegate methods to run.
Crazy or what?
Ok - I'm editing this answer now, as even though the above is all correct, where a navigationController is being used, selected with each tabBarButton touched, the didSelectViewController delegate method will, when you try to NSLog(#"%#", viewController); only show you that you have selected the UINavigationController class?
So, the total solution, just to add more complexity, is to subclass the UINavigationController for each viewController that you want to monitor, (do something) when the tabBarbutton has been touched.
It works for me anyhow. And, if anyone can nit-pick through the above dribble, they might find an aspect that's useful - and that's enough for me - seeing as I find this site utterly useful too.
Put [self setDelegate:self]; in your ViewDidLoad or somewhere where the object get's initialized

Resources