When I try to push a UIViewController subclass (MissionViewController) onto a UINavigationController from within my root view controller, the viewDidLoad method is only called if I reference the underlying view after initializing the view controller. However, even with the method called, the screen is not updated.
Both the view controller and the navigation controller are not nil. The file owner in MissionView.xib is set to MissionViewController. The view property of the file owner points to the nib view. I've verified that the topViewController property of the navigation controller points to the MissionViewController object.
If I change MissionViewController to be the root view controller, the view loads fine. Saw several similar posts but still having problems.
rootViewController.h
#interface TreeGraphController : UIViewController
{
MissionViewController *missionViewCtrlr;
}
#property(nonatomic, retain) MissionViewController *missionViewCtrlr;
rootViewController.m
#import "MissionViewController.h"
...
if (!missionViewCtrlr)
{
MissionViewController *ctrlr = [[MissionViewController alloc] initWithNibName:#"MissionView" bundle:nil];
ctrlr.view.hidden = NO;
self.missionViewCtrlr = ctrlr;
[ctrlr release];
}
myAppDelegate *del = [[UIApplication sharedApplication] delegate];
[del.navigationController pushViewController:missionViewCtrlr animated:NO];
What about using
[self.navigationController pushViewController:missionViewCtrlr];
in the rootViewController? I assume rootViewController has been pushed to a UINavigationController and that the navigation controller's view was added as a subview of self.window in the app delegate?
Btw, you may simplify part of your code by just saying
if(!missionViewCtrlr)
self.missionViewCtrlr = [[MisionViewController alloc] init...];
Related
I want to navigate to the initial view from almost any View Controller in my storyboard. I don't want to create a segue manually for every view to the initial view. Is there a nice and handy way to do this in one line, or maybe a method that my other VC could inherit?
The best way to do this (in my opinion) would be using a UINavigation Controller.
Make the UINavigation Controller the root view controller of your storyboard, then connect it to your "actual" root view controller.
Then, from any view controller that has been pushed with the navigation controller, you can do:
[self.navigationController popToRootViewControllerAnimated:YES]; // or NO
You can keep a strong reference to the initial/root view controller you want in the AppDelegate, and then setup a method within it that you call from anywhere to switch the current view controller to your initial controller.
You would do something like this in the AppDelegate.h file:
#property (strong, nonatomic) MyInitialViewController *initialViewController
- (void)switchToInitialViewController;
In the AppDelegate.m you can do:
- (void)switchToInitialViewController
{
self.window.rootViewCotroller = self.initialViewController;
}
And then you can do this from any method:
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
[appDelegate switchToInitialViewController];
And that, hopefully, helps. :)
Why don't you go for
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if([segue.identifer isEqualToString:#"identifier"]) {
// Put your code here....
}
}
Every ViewController have this kind of method inside and you can move to other controller or your intial Controller. and if you're using NavigationController then you can go for
#Eric answer.
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.
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 am trying to push a new root controller to a navigation stack, but using a side reveal menu.
My app delegate has the following:
welcomeViewController = [[MyWelcomeViewController alloc] initWithNibName:#"MyWelcomeViewController" bundle:nil];
navController = [[UINavigationController alloc] initWithRootViewController:welcomeViewController];
navController.navigationBarHidden = YES;
// Then we setup the reveal side view controller with the root view controller as the navigation controller
self.revealSideViewController = [[PPRevealSideViewController alloc] initWithRootViewController:navController];
[self.revealSideViewController setDirectionsToShowBounce:PPRevealSideDirectionNone];
[self.revealSideViewController setPanInteractionsWhenClosed:PPRevealSideInteractionContentView | PPRevealSideInteractionNavigationBar];
// Then we make the window root view controller the reveal side view controller
self.window.rootViewController = self.revealSideViewController;
Once the welcome view controller is displayed, the user logs in. Once logged in the following process runs again from the App Delegate.
self.navController.navigationBarHidden = NO;
[self.navController setTitle:#"Home"];
[self.navController pushViewController:homeViewController animated:NO];
I then have a side view controller setup which is a table view with custom cells setup.
When a row is selected I need to push a new root controller onto the navigation controller. I try this by using the following in the table view for the cell selected.
MyAccountViewController *accountViewController = [[MyAccountViewController alloc] init];
[self.navigationController setViewControllers:[NSArray arrayWithObject:accountViewController] animated:NO];
Unfortunately this does not do anything. If I add the code to the App Delegate and then call the method from the table view controller then it works, however not from the .m file for the table view itself. Adding a log I can see the above is run, just does not do anything.
I am unsure if I need to do anything different on the above. For example, completely pop the views currently shown, then create the navigation controller and PPRevealSideViewController all over again. If I am supposed to, I am unsure how to pop all the current views to then push the new to the window, not from the AppDelegate.
The reason I do not want this in the App Delegate is because it is the incorrect way to approach this, and I would then need a separate method for each new root controller I would like to push from the menu, so the App Delegate would become very large.
Check UINavigationController.h:
#interface UIViewController (UINavigationControllerItem)
#property(nonatomic,readonly,retain) UINavigationController *navigationController; // If this view controller has been pushed onto a navigation controller, return it.
It means when you do myViewController.navigationController you will either get nil if myViewController is not pushed to any navController or the navController reference myViewController is pushed into.
As I understand your tableViewController is not pushed into the navController stack, that means you can't get the navController with tableViewController.navigationController. Instead you'll need to use anyViewControllerInTheStack.navigationController or if the navController is the rootViewController of your keyWindow, by
((UINavigationController*)[[UIApplication sharedApplication] keyWindow].rootViewController)
Add something like this to your AppDelegate.h:
#define XAppDelegate ((AppDelegate *)[[UIApplication sharedApplication] delegate])
Now you can access any iVar of AppDelegate from any .m file in your project.
MyAccountViewController *accountViewController = [[MyAccountViewController alloc] init];
[XAppDelegate.navController pushViewController:accountViewController animated:NO];
Make sure you add the correct imports.
One more thing: It's good to pop the login window from your navcontroller once you are done Logging in.
Hope this helps.
I am working on an app that at launch checks for valid login credentials, and if they are found and not expired the main split view controller is displayed, and if not a login screen should be displayed.
Each part is working fine separately, but I am struggling with the best way at launch time to select the proper view to display.
I have tried setting up a modal segue from the root view controller, and in my application:didFinishLaunchingWithOptions: function in the App Delegate, calling this:
// Segue to the login view controller...
if (loginNeeded) {
[self.window.rootViewController performSegueWithIdentifier:#"LoginScreen" sender:self];
}
This logically should work, but triggering segues from within the app delegate seems to be impossible.
What is the ideal place and technique for handling this?
You could try a custom segue, as per this post hiding-a-segue-on-login-process.
Alternatively if you're desperate to have the login display before the split view controller loads try something along the following lines...
Create your login screen on the main storyboard as, say, a subclass of UIViewController. Make sure it is the initial scene (check Is Initial View Controller).
On the storyboard, create a new segue from your login class to the original SplitViewController. Give it an identifier, 'Load SplitViewController' and a segue custom class name which we'll call FullyReplaceSegue.
In your login class .m file, add code to be called once the user has logged in:
[self performSegueWithIdentifier:#"Load SplitViewController" sender:self];
Create the new segue class, based on UIStoryboardSegue and name it FullyReplaceSegue as per above.
.h file
#import <UIKit/UIKit.h>
#interface : UIStoryboardSegue
#end
.m file
#import "FullyReplaceSegue.h"
#implementation FullyReplaceSegue
- (void)perform
{
UIViewController *dest = (UIViewController *) super.destinationViewController;
UIWindow *window = [UIApplication sharedApplication].keyWindow;
window.rootViewController = dest;
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
UISplitViewController *splitViewController = (UISplitViewController *)dest; // assumes we're transitioning to a UISplitViewController!
UINavigationController *navigationController = [splitViewController.viewControllers lastObject];
splitViewController.delegate = (id)navigationController.topViewController;
}
}
#end
Here's how I did it.
In didFinishLaunchingWithOptions:
//save the root view controller
[[self window] makeKeyAndVisible];
UINavigationController *navigationController = (UINavigationController*) self.window.rootViewController;
rootController = [[navigationController viewControllers] objectAtIndex:0];
Somewhere else in the app delegate:
[rootController performSegueWithIdentifier:#"fileSegueID" sender:self];
Then, in the storyboard, create a segue from the view that gets assigned as "rootController", to the desired optional view, and give that new segue the id fileSegueID. It takes some debugging to make sure the rootController variable gets assigned to the correct view.
Maybe a little late, but I was looking for the same suggestions. Here's what I wound up doing.
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Signup" bundle:nil];
if(isLoggedIn) {
UINavigationController *navigationController = (UINavigationController*) self.window.rootViewController;
IndexController *ivc = [storyboard instantiateViewControllerWithIdentifier:#"IndexController"];
[navigationController pushViewController:ivc animated:NO];
}
Why don't you load the screen that would be visible assuming proper and non-expired log-in credentials (by setting it as the root view controller of the window), and then in viewDidLoad of that first view controller, check if an update to the login credentials are needed. If so, segue into the login view controller.
Yes, it can be used, if you get a reference to the segue's parent view controller. You can get it like this:
UINavigationController *navigationController = (UINavigationController*) self.window.rootViewController;
[[[navigationController viewControllers] objectAtIndex:0] performSegueWithIdentifier:#"LoginScreen" sender:self];
This will only work if the index in viewControllers array matches the one of your view controller and if it exists of course. In this case is the first one (in the array and storyboard).
The segue ("LoginScreen") must not be attached to an action. The way you do this is by control-dragging from the file owner icon at the bottom of the storyboard scene to the destination scene. A popup will appear that will ask for an option in “Manual Segue”; pick “Push” as the type. Tap on the little square and make sure you’re in the Attributes Inspector. Give it an identifier which you will use to refer to it in code.