I'm trying to call the setSelectedIndex of my tabBarViewController via a delegate method in another viewController.
I've got the whole delegate part working, and the method call in my tabBarViewController correctly Logs to the console as being called.
The problem I'm having is, that it doesn't change my tabBar to the correct index. This is the method being called in my tabBarViewController.m
-(void)passNewSelectedIndex{
NSLog(#"delegate method called"); //This correctly outputs to the logs when the delegate method is called.
[self setSelectedIndex:1];
}
But it doesn't work.
Now, if I call [self setSelectedIndex:1]; in my viewDidLoad of the tabBarViewController.m, it works, and it correctly displays the tab at index:1. But that's obviously not where I want to set it. I think I might be missing something really obvious, but I just cant figure out what.
EDIT Posted the code for the concerned .h's and .m's
ScoreViewController.h:
#protocol ScoreViewControllerDelegate <NSObject>
- (void)passNewSelectedIndex;
#end
#interface CEWSoreViewController : UICollectionViewController{
id <ScoreViewControllerDelegate> scoreDelegate;
}
#property (nonatomic, weak) id<ScoreViewControllerDelegate> scoreDelegate;
#end
ScoreViewController.m:
- (void)passNewSelectedIndex{
CEWTabBarViewController *instanceOfTabBarCont = [[CEWTabBarViewController alloc] init];
self.scoreDelegate = instanceOfTabBarCont;
NSLog(#"I was called, hurray");
[instanceOfTabBarCont passNewSelectedIndex];
}
TabBarViewController.h
#import "CEWFinalScoreViewController.h"
#interface CEWTabBarViewController : UITabBarController <UITabBarControllerDelegate, ScoreViewControllerDelegate>{
}
#end
tabBarViewController.m:
-(void)passNewSelectedIndex{
NSLog(#"delegate method called");
NSLog(#"self.tabBar: %#", self.tabBar);
[self setSelectedIndex:1];
}
AppDelegate.m
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
self.window.backgroundColor = [UIColor whiteColor];
CEWTabBarViewController *tabViewController = [[CEWTabBarViewController alloc] init];
self.window.rootViewController = tabViewController;
[self.window makeKeyAndVisible];
return YES;
Hope this makes a bit more sense now.
The problem is exactly as I expected:
- (void)passNewSelectedIndex{
CEWTabBarViewController *instanceOfTabBarCont = [[CEWTabBarViewController alloc] init];
self.scoreDelegate = instanceOfTabBarCont;
NSLog(#"I was called, hurray");
[instanceOfTabBarCont passNewSelectedIndex];
}
You're instantiating a new tab bar controller and calling the passNewSelectedIndex method on that. The problem is, that tab bar controller isn't the one controlling the tab bar your view controllers are in.
Try this:
[(CEWTabBarViewController*)self.tabBarController passNewSelectedIndex];
Depending on your navigational setup, you may need this instead:
[(CEWTabBarViewController*)self.view.window.rootViewController passNewSelectedIndex];
We would need a LOT more of your project to figure out exactly the best way to get a reference to the right Tab Bar Controller (I'm not recommending you do this), but the point is, we have to get a reference to the already instantiated tab bar controller that is already controlling our tab bar, not simply instantiate a new one and expect it to magically control our current tab bar.
The reason it works in viewDidLoad of your tab bar controller is because every instance of your tab bar controller calls viewDidLoad on its own. When moved into passNewSelectedIndex, this method is only called when someone else calls it.
selectedIndex is a property of UITabBarController
[self setSelectedIndex:1];
This would work only if the current controller is a subclass of UITabBarController.
For your scenario, you would have to pass the UITabBarController as a parameter
-(void)passNewSelectedIndex:(UITabBarController *)tabBar {
NSLog(#"delegate method called");
[tabBar setSelectedIndex:1];
}
If the view controller which declares your delegate method is itself a subclass of UITabBarController, then the [self setSelectedIndex:1];
would result in calling its own tabBar and not the one on which the delegate method is set.
Related
I am using this code for the navigation view controller in the start
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
splashViewController *rootVC = [[splashViewController alloc]initWithNibName:#"splashViewController" bundle:nil];
self.navigator = [[UINavigationController alloc] initWithRootViewController:rootVC];
self.window.rootViewController = self.navigator;
[self.window makeKeyAndVisible];
after that i am using this simple method to push to next view
ScoreboardListViewController *SLvc = [[ScoreboardListViewController alloc]initWithNibName:#"ScoreboardListViewController" bundle:nil];
[self.navigationController pushViewController:SLvc animated:YES];
and using this to pop out from the view
[self.navigationController popViewControllerAnimated:YES];
but when i poppet and again push to the same view with different property values then viewdidload method did not run
it only runs if i just to any other view and then push to this view
i was not able to understand this abnormal behaviour. as when ever i push to any view then this viewdidload should be executed.....
for example i am doing this to get to the view
->
SLvc.trigger = #"sold";
SLvc.lblHeader.text = value;
[self.navigationController pushViewController:SLvc animated:YES];
-> then i popped out by back button by using this
[self.navigationController popViewControllerAnimated:YES];
-> then i push to the same view with different property
SLvc.trigger = #"earning";
SLvc.lblHeader.text = value;
[self.navigationController pushViewController:SLvc animated:YES]
and this time viewdidload didn't run.
This is because you are holding a strong pointer to SLvc controller. When you pop it, the view controller is retained. Initializing a new controller every time you push will solve your problem.
// below method called when pop out from the view
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
}
You can try these in your viewcontroller:
-(void) viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
// your code here
}
or
-(void)awakeFromNib
{
[super awakeFromNib];
// your code here
}
viewDidLoad() -Called after init(coder:) when the view is loaded into memory.
Similar to viewWillAppear, this method is called just before the view disappears from the screen. And like viewWillAppear, this method can be called multiple times during the life of the view controller object. It’s called when the user navigates away from the screen – perhaps dismissing the screen, selecting another tab, tapping a button that shows a modal view, or navigating further down the navigation hierarchy
viewDidLoad is called exactly once, when the ViewController is first loaded into the memory. This is where you want to instantiate any instance variables and build any views that live for the entire lifecycle of this ViewController. However, the view is usually not yet visible at this point.
However, viewWillAppear gets called every time the view appears.
I have an app which displays a simple tableview and I wanted to add the SWRevealViewController as well.
In my appDelegate, before I added the SWReveal VC, I was setting my tableViewController like so...
In didFinishLaunchingWithOptions:
STRTableViewController *tableViewController = [(UINavigationController *)self.window.rootViewController viewControllers][0];
self.delegate = tableViewController;
and then again in the below method:
- (void)loadTableViewData
{
UINavigationController *navVC = (UINavigationController *)self.window.rootViewController;
STRTableViewController *tableVC = navVC.childViewControllers[0];
[tableVC loadTableData]
}
Obviously when I put the SWRevealViewController to the front of the line, this no longer works as it is now trying to call loadTableData from the wrong view controller.
I've tried several ways and keep coming up short. How do I go about accessing the tableViewController now that it is not the first view controller?
If you need more code or logs or anything I'll be happy to post additional info. I have a feeling the answer is right there, I just don't have the experience to see it.
Also, just to be clear, now in the storyboard it goes from Reveal View Controller to Navigation Controller (the tableview's nav VC/ sw_front) and also to the sw_rear VC. Before it simply started with the Navigation Controller.
Thanks!
There's a bunch of ways you can go about keeping a reference to this.
The simplest would be just to keep a reference to the view controller in the AppDelegate.m
So you add a property
#property (nonatomic, strong) STRTableViewController *tableViewController;
Then, whenever and wherever you are instantiating and setting that table view controller, just do something like:
AppDelegate *delegate = [[UIApplication sharedApplication] delegate];
delegate.tableViewController = justCreatedTableViewController;
You'll need to #import "AppDelegate.h" to access the app delegate in other classes where you want to do this.
Then to access it you can just do something like:
- (void)loadTableViewData
{
[self.tableViewController loadTableData]
}
I have a HomeView and a HomeDropDownView.
HomeDropDownView is shown as a drop-down view over the HomeView.
HomeView is a delegate of HomeDropDownView.
When I do an action in HomeDropDownView I want to call a delegate method in HomeView and have that delegate method present a third view controller, TestViewController from it's navigation controller.
If I try to launch TestViewController from anywhere in the class it works fine - except from the delegate method.
There are animations in HomeDropDownView but putting the call to the delegate method in the complition does not make the view controller appear. And in the case that I'm using this the animation's don't fire anyway; there's only a resizing without animation.
TestViewController's init does get called as well as the viewDidLoad but not the viewWillAppear and the view dose not appear.
Code:
HomeDropDownView
- (void)finalAction {
...
[self callDelegateAction];
...
- (void)calldelegateAction {
if ([self.delegate respondsToSelector:#selector(launchTestView)] ) {
[self.delegate launchTestView];
} else {
DLog(#"Error out to the user.");
}
}
HomeView
- (void)launchTestView {
//[self listSubviewsOfView:self.parentViewController.view];
NSLog(#"delegate method | self: %#", self);
TestViewController *tvc = [[TestViewController alloc] initWithNibName:#"TestViewController" bundle:nil];
//[self.navigationController presentViewController:tvc animated:YES completion:nil];
//[self.view.window.rootViewController presentViewController:tvc animated:YES completion:nil];
//[self.navigationController pushViewController:tvc animated:YES];
AppDelegate *appdelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
[appdelegate.tabBarController.navigationController presentViewController:tvc animated:YES completion:^() {
NSLog(#"Done!");
}];
}
None of the above approaches work. But if I put the exact same code into the viewDidAppear or put it in a button action method, it will work fine. At the time of calling the delegate method's self is HomeView and all the subviews, including the nav controller do seem to be there. This is in a tabcontroller-based project but I think that any of the above are acceptable ways to call the nav controller still.
What am I missing? Why does my delegate method not want to push/present a viewcontroller on HomeView's Nav controller? It's probably something I'm missing but I can't find a reason in the Apple Docs or any other thread.
Thanks for the help!
Sadly this turned out to be that HomeView was being changed underneath the execution of the message. So by the time the HomeView got the message call it was no longer the same HomeView object that had requested action in the first place. So it was not the same delegate.
This was done so that it would appear to the user that the same view was being used for different things.
But this is a good example of why you should not destroy and re-create critical views. We should have been using the same view and reloading the objects instead if we knew that we would be sending messages. Or had some notion of a control structure.
I'm obviously missing something...
In my iOS app, I push a UIViewController onto a navigation controller:
MyViewController *mvc = [[MyViewController alloc] initWithNibName:#"MyViewController"];
[self.navigationController mvc animated:YES];
MyViewController displays fine, and I can see and use the navigationBar, but when I try to get a pointer back to the navigation controller from within my view controller, I get a nil result.
UINavigationController *nav = [self navigationController];
if (!nav) {
NSLog(#"no nav");
}
I've been beating my head against this all day, but can't see that I'm doing anything wrong. I get no warnings or errors in Xcode. Am I completely missing something?
TIA: john
The navigationController won't be set properly on viewDidLoad. You have to check it in viewDidAppear or at some later stage. Which method are you calling to [self navigationController] in?
The reason for this is that when viewDidLoad is called, the UINavigationController is still processing the pushViewController:animated: method. It would appear to set the navigationController property after it initialises the controller's view. I can't recall whether the property is set by the time viewWillAppear runs, but it should definitely be set by viewDidAppear.
id rootViewController = [[[[[UIApplication sharedApplication] keyWindow] subviews] objectAtIndex:0] nextResponder];
I have a split-view app that allows a user to select and display a thumbnail of a chosen image. I have placed a UIButton in the detailViewController using Interface Builder. When this button is pressed, I would like to have it change to a full screen view of the image. I have set up a new View Controller, called FullViewController and thought I had everything connected. The problem is that the navigation controller is null. I adjusted the AppDelegate.m to the following:
AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after app launch.
// Set the split view controller as the window's root view controller and display.
self.window.rootViewController = self.splitViewController;
UINavigationController *nvcontrol =[[UINavigationController alloc] initWithRootViewController:fullViewController];
[window addSubview:nvcontrol.view];
[self.window makeKeyAndVisible];
return YES;
}
This is the function in the DetailViewController.m which is called when the button is pressed. The navigation controller comes up null in here.
//Function called when button is pressed - should bring up full screen view
- (IBAction) pressFullViewButtonFunction: (id) sender{
//viewLabel.text = #"Full View";
if (fullViewController == nil){
FullViewController *fullViewController = [[FullViewController alloc] initWithNibName:#"FullViewController" bundle:[NSBundle mainBundle]];
NSLog(#"fullViewController is %#", fullViewController);
self.fullViewController = fullViewController;
}
NSLog(#"self.navigationController is %#",self.navigationController);//this is null
[self.navigationController pushViewController:self.fullViewController animated:YES];
}
I'm not sure how to fix this. I've tried adding in the couple lines in the AppDelegate, but when it runs, the table in the root view doesn't show up and it no longer properly switches between portrait and landscape views.
I have the rest of the code readily available if that would help clarify. Just let me know!
Thanks.
From the code you post it is not possible to identify the problem, but two common reasons for self.navigationController to be nil are:
you did not push the object behind self on to the navigation controller in the first place; indeed it seems so, since the navigation controller is added as a subview of the split view controller; possibly you mean the opposite... not sure...
(sub-case of 1) you showed the object behind self using presentViewControllerModally.
When I say "the object behind self" I mean the instance of the class where pressFullViewButtonFunction is defined.
If you need more help, post the code where you push your controllers on to the navigation controller...
On a side note, if you do:
UINavigationController *nvcontrol =[[UINavigationController alloc] initWithRootViewController:fullViewController];
and nvcontrol is not an ivar, then you have a leak.
Hope this helps...