Reload ViewController by clicking on TabBarItem - objective-c

I'm kinda desperate right now :/
I have a Tab Bar Controller with 4 Items. In the 4. Tab I included a webView which shows a list of pdf's. If I open a PDF in the webView there is no way to go back to the main webView with the links. Is there a way by re-clicking the 4. TabBar to reload the View? If I change from the 3. to the 4. tabbar it works (viewWillAppear).
Someone told me, that the following method should work:
- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController{
if ([viewController isKindOfClass:[UIColor class]]) {
//Try this if you're pushing the webView in another ViewController
[viewController.navigationController popToRootViewControllerAnimated:YES];
//or access to your webView and call goBack();
}
}
but actually I have no idea in which file I should insert that method. (See print Screen)
Thanks a LOT in advance for your help guys!

Subclass UITabBarController
1.1. Cmd+N and create a new instance of NSObject class, and name it TabBarController
1.2. in TabBarController.h replace NSObject so that it reads #interface TabBarController : UITabBarController <UITabBarControllerDelegate>
1.3. in TabBarController.m add this:
- (id) init
{
self = [super init];
if (self)
{
self.delegate = self;
}
return self;
}
1.4. and this
- (void)tabBarController:(UITabBarController *)tabBarController
didSelectViewController:(UIViewController *)viewController
{
// Is this the view controller type you are interested in?
if ([viewController isKindOfClass:[MehrViewController class]])
{
// call appropriate method on the class, e.g. updateView or reloadView
[(MehrViewController *) viewController updateView];
}
}
1.5. In IB, Inspection, change the class of Tab Bar Controller to your TabBarController (instead of UITabBarController)
1.6. You also need to include MehrViewController.h in TabBarController.m
Edit
in MehrViewController.m (as you posted in your question, assuming it has a webView)
// An example of implementing reloadView
- (void)reloadView {
[self.webView reload];
}

Related

How to call method from a tabbar rootview controller

I have 3 tabbar items, each a view controller. First, Second, and Third viewcontroller. I need First ViewController to call a method that updates the tableview on second view controller view. I'm not sure what's the correct way to handle this. I tried a sharedInstance, but what I think is happening is two instances are being created and that view controller that the first VM is using isn't the same VM that is actually being used in the app, which would explain why my tableview isn't updating.
Basically when I upload a file in First View Controller, I need the Second VM to update the tableview and show the file's upload progress. Kind of like when a song is purchased on iTunes. These are UINavigationViewControllers for tab items.
I tried this:
+ (SecondViewController *)sharedInstance {
// Singleton implementation
static SecondViewController* instance;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[SecondViewController alloc] init];
});
return instance;
}
- (void)viewDidLoad
{
[super viewDidLoad];
UploadTableView.dataSource = self;
UploadTableView.delegate = self;
[S3UploadClientManager tm].delegate = self;
}
You don't want the controllers to communicate with each other directly. If you are segueing to another view you can use prepareForSegue. If you don't want to use that I suggest you either update a file or a database that both controllers have access to as to avoid direct interaction and keep the mvc architecture.
You could implement the tabBarController delegate method:
- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController {
if([viewController isMemberOfClass[SecondViewController class]]) {
//pass the data here
}
}

Problems with storyboard

recently I started using storyboard and I've the following situation: I want to set the text of an UILabel from the AppDelegate. So I created an instance of my ViewController
UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:#"MainStoryboard"
bundle: nil];
ViewController *controller = (ViewController*)[mainStoryboard
instantiateViewControllerWithIdentifier: #"mainViewController"];
myViewController = controller;
[window addSubview:myViewController.view];
[window makeKeyAndVisible];
and called the following method from the delegate
- (void) updateParameterLabel:(NSString *)parameter {
NSLog(#"URL-2: %#", parameter);
parameterLabel.text = parameter;
}
But the parameter is not shown in the UI.
Another think, which is kind of strage:
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
NSLog(#"View did Appear");
}
The "View did appear" is logged twice ...
Any hints?
Regards,
Sascha
Setting the text of a UILabel from your application delegate isn't great design. Your view controllers should be managing the content of your views, hence their name. Typically your storyboard is instantiated automatically, and you don't need any of the storyboardWithName et code you've got, assuming you're working with Apple's default templates.
Maybe think about re-architecting your application to follow the 'model-view-controller' pattern more strictly, and also look at how Apple instantiate storyboards automatically (just create a new storyboard project in XCode to see this).
If you still want to make it work, make the UILabel a property of your viewcontroller and set the label by using
In delegate :
- (void) updateParameterLabel:(NSString *)parameter {
NSLog(#"URL-2: %#", parameter);
[myViewController updateParemeter:parameter];
}
In myViewController:
- (void) updateParameterLabel:(NSString *)parameter {
NSLog(#"URL-2: %#", parameter);
parameterLabel.text = parameter;
[self.view setNeedsDisplay];//edit
}
So use the viewController to update your label. Of course you need the label as a property in your viewController
For what I see you are trying to update the label before it appears, so why don't you try calling your updateLabel method in the viewWillAppear, it would be something like this
-(void)viewWillAppear:(BOOL)animated{
[self updateParameterLabel:#"Some Text"];
[super viewWillAppear:YES];
}
And updateParameterLabel has to be implemented in the viewController.

Objective C: How to disable user interaction to all of tab bars except one?

As what the title suggests, I would like to be able to lock all my tab bars except for one. And only after the user completes an action will I enable all the rest of the tab bars. How can I do that?
I haven't tried it, but according to the docs, you can return NO from the tabBarController:shouldSelectViewController: delegate.
[UPDATE] I just tried that out of curiosity - it seems to work fine. Create a new project from the "Tab bar application" template and then go to the -viewDidLoad of your FirstViewController. Add this line:
[self.tabBarController setDelegate:self];
and then implement the delegate method:
-(BOOL)tabBarController:(UITabBarController *)tabBarController shouldSelectViewController:(UIViewController *)viewController {
if (userHasCompletedAction) {
return YES;
}
return NO;
}
Don't forget to conform to <UITabBarControllerDelegate> in your .h file!
Hope that helps.
You have to implement this method
- (void)tabBarController:(UITabBarController *)tabBarController1 didSelectViewController:(UIViewController *)viewController {
if ([tabBarController1 selectedIndex]==0) {
UITabBarItem *tabBarItem = [[[[self tabBarController]tabBar]items] objectAtIndex:1];
[tabBarItem setEnabled:FALSE];
}
}
You have to do something like this for disabling your required tabbar items.
The method tabBar:didSelectItem: in UITabBarDelegate could help.

Objective C: How to reload a view controller's table view when tab is selected

I need to reload the data in a view controller when it's tabbar is clicked.
I am using the UITabBarControllerDelegate method as below:
- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController
{
if (tabBarController.selectedIndex == 3)
{
[(SomeViewController *)viewController getData];
}
}
where 'getData' is an instance method in SomeViewController class. However when I run my app, I get the following error
2011-07-01 02:12:11.193 onethingaday[19169:207] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[UINavigationController getData]: unrecognized selector sent to instance 0x600d500'
Can anyone advise me how I can overcome this issue? I just need to trigger the 'getData' method when tabbarcontroller.selected index ==3
It seems to me from the error message you get, that you use a UINavigationController in your tab controller; in this case, you cannot send directly the getData message to it; you should first find out which view controller under the UINavigationController should receive that message. (This is not actually related to the tab bar selectedIndex)
I don't know how your UINavigationController is organized, but you could do:
- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController {
if (tabBarController.selectedIndex == 3) {
//-- option 1: getData goes to the first view controller in the UINavigationController:
[[(SomeViewController*)[(UINavigationController*)viewController topViewController] getData];
//-- option 2: getData goes to the last view controller in the UINavigationController (the visible one):
[[(SomeViewController*)[(UINavigationController*)viewController visibleViewController] getData];
}
}
If you give more details about the organization of your UINavigationController I can help further identifying the right option.
Anyway, as you can see from the casts, there is something that is not fully ok with your design. I would strongly suggest using a notification for that. I.e., your SomeViewController registers itself for a notification of a given type :
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(someSelector:)
name:ShouldGetDataNotification
object:nil];
and the tab bar controller sends the notification for your controller to react upon:
- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController {
if (tabBarController.selectedIndex == 3) {
[[NSNotificationCenter defaultCenter] postNotificationName:ShouldGetDataNotification object:nil];
}
....
}
Look at this post.
See the solution to InterfaceBuilder - UIViewController subclass not recognized as subclass
- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController
{
if (tabBarController.selectedIndex == 3)
{
[[[(UINavigationController *)viewController viewControllers] objectAtIndex:2] getData];//2 for 3rd tabbar since 0,1,2
}
}
You could implement the -viewWillAppear method in your UITableViewController subclass. That should be called automatically when the UITabBarController switches to the view. It should look something like this:
- (void)viewWillAppear {
[super viewWillAppear];
[self getData];
}

viewDidLoad in NSViewController?

On the iPhone I use UIViewController's viewDidLoad to run code to set up the view.
How can I do that with NSViewController?
I've tried loadView but it doesn't work...
I figured it out within minutes of posting my comment. Adding my finding as an answer because it is an example which is missing in the docs. The below code will give you the viewDidLoad method that you want. Its so easy in a way that i wonder why Apple has not implemented it yet in OS X.
- (void)viewWillLoad {
if([NSViewController instancesRespondToSelector:#selector(viewWillLoad)]) {
[super viewWillLoad];
}
...
}
- (void)viewDidLoad {
if([NSViewController instancesRespondToSelector:#selector(viewWillLoad)]) {
[super viewDidLoad];
}
}
- (void)loadView {
BOOL ownImp = ![NSViewController instancesRespondToSelector:#selector(viewWillLoad)];
if(ownImp) {
[self viewWillLoad];
}
[super loadView];
if(ownImp) {
[self viewDidLoad];
}
}
Original source: http://www.cocoabuilder.com/archive/cocoa/195802-garbage-collection-leaks-and-drains.html
As of OS X 10.10, viewDidLoad is available and supported on NSViewController.
Prior to that, you had to go by this nugget in Snow Leopards' release notes:
Advice for People who Are Looking for -viewWillLoad and -viewDidLoad Methods in NSViewController
Even though NSWindowController has -windowWillLoad and -windowDidLoad methods for you to override the NSViewController class introduced in Mac OS 10.5 does not have corresponding -viewWillLoad and -viewDidLoad methods. You can override -[NSViewController loadView] to customize what happens immediately before or immediately after nib loading done by a view controller.
As of OSX 10.10 (Yosemite), there is now a -viewDidLoad, -viewWillAppear, -viewDidAppear, -viewWillDisappear in NSViewController. See WWDC 2014 - Storyboards and Controllers on OS X session for more info, to find out when each of them gets called, etc.
Here's the relevant bit from the 10.10 header docs about -viewDidLoad:
Called after the view has been loaded. For view controllers created in
code, this is after -loadView. For view controllers unarchived from a
nib, this is after the view is set. Default does nothing.
- (void)viewDidLoad NS_AVAILABLE_MAC(10_10);
why don't you try this:
- (void)awakeFromNib {
//setup code
NSLog(#"hello there");
}
It looks like in 10.10, viewDidLoad is now in NSViewController.
hmm actually I would also do this...
- (void)viewWillLoad {
if (! bool_viewwillload) {
// execute the code
bool_viewwillload = true;
}
}
- (void)viewDidLoad {
if (! bool_viewdidload) {
// execute the code
bool_viewdidload = true;
}
}
and then just make the load view like this
- (void)loadView {
[self viewWillLoad];
[super loadView];
[self viewDidLoad];
}