I have a UIViewController within a UINavigationController, and I'm pushing a second UIViewController onto the navigation stack from a XIB file. This XIB also includes a UINavigationItem for the second view controller's title and button, but these are ignored when loading the XIB. Any ideas?
Here is my code (photoViewController is the second viewController)
- (void) displayPhotoWithId:(int)photoId {
if (_photoViewController == nil) {
self.photoViewController = [[[PhotoViewController alloc] initWithNibName:#"PhotoView" bundle:[NSBundle mainBundle]] autorelease];
}
_photoViewController.photoId = photoId;
[self.navigationController pushViewController:_photoViewController animated:YES];
}
You can wire this up from interface builder. Add something like this to your view controller:
IBOutlet UINavigationItem* navigationItem;
Then wire it up to the view controller outlets (from File Owner, presumably, to the UINavigationItem in your XIB). I have done this and it works fine. I suppose UINavigationController looks for the 'navigationItem' automagically.
Navigation item is ignored because it has nothing to do with this controller, it's just happened to be in this controller's XIB. Controller's navigation item is created on instantiation. You have to setup title and navigation buttons in code (in viewDidLoad for example) because controller doesn't have navigationItem outlet. UINavigationItem in XIB works only inside UINavigationController's root controller.
Related
I'm not sure why but in Xcode 5 working on a project of IOS6.1 I have a button connected to a IBAction in which I'm trying to navigate to a new view controller.
I've tried two different codes to create the viewController and then push it to the navigation in both cases the view controller is not nil and both cases the viewController doesn't appear.
first try: with story Id - I've set the story id of the view controller to imageCapture and set the class to VSImageCaptureViewController
VSImageCaptureViewController* imageCaptureViewController = (VSImageCaptureViewController*)([self.storyboard instantiateViewControllerWithIdentifier:#"imageCapture"]);
[self presentViewController:imageCaptureViewController animated:NO completion:nil];
second try: with the name of the viewcontroller
VSImageCaptureViewController *imageCaptureViewController = [[VSImageCaptureViewController alloc] initWithNibName:#"VSImageCaptureViewController" bundle:nil];
[self.navigationController pushViewController:imageCaptureViewController animated:YES];
can you see something wrong or do you think I forgot to initialize something
Check to see if self.navigationController is nil.
If it is nil that means that you are not running within the context of a UINavigationController (the system sets this property for you when the UIViewController is added to a nav stack).
If this is the case then you have not properly set up a UINavigationController.
Note that you can not set the navigationController property yourself. The systems sets it for you when the UIViewController is added to a UINavigationController's stack (and sets it to nil when it is removed from the stack).
To set this up you will usually create a UINavigationController instance right after you create your main view controller.
UIViewController *mainViewController = ...;
UINavigationController *mainNavController = [[UINavigationController alloc] initWithRootViewController:mainViewController];
// now present the mainNavController instead of the mainViewController
If you are using storyboards you would drag out a UINavigationController instance and replace the default root view controller with an instance of your mainViewController.
I created a view in IB with a navbar and a table. On the navbar I put two buttons, cancel and done. I use this view like a modal view with:
[self presentModalViewController:controller animated:YES];
My problem is when I use:
[self.navigationItem.rightBarButtonItem setEnabled:YES];
to enable the right button. It doesn't work.
Have I to set a delegate? what code passages I have to do? It works if I create an IBOutlet for the right button and I use [doneButton setEnabled:YES], but I think this isn't the proper way.
In order to place a navigation bar on your modal view controller in interface builder (and set up bar button items that call actions in your detail view controller), you need to go through a level of indirection (your navigation bar will be in one .xib, and the details of your detail view will be in a different xib):
create a xib file containing a navigation controller object, and set its root view controller to be your detail view controller that you want to display modally with a navigation bar.
add bar button items to the detail controller's navigation bar and hook them up to IBActions in your detail view controller object.
your detail view controller will need to be in a separate .xib file
create a "loader" object that just exists to hold the navigation controller iboutlet, and set it to be the File's Owner object of that xib:
#interface Loader : NSObject
#property (nonatomic, retain) IBOutlet UINavigationController *navVC;
#end
#implementation Loader
#synthesize navVC;
- (void) dealloc
{
[navVC release];
[super dealloc];
}
#end
Your xib file containing the navigation controller will look like this:
Make sure the navigation controller object is conntected to the "Loader" object's navVC outlet, and make sure the bar button items are connected to your detail view controller's desired IBActions.
Then you present this whole thing using this code:
Loader *loader = [[[Loader alloc] init] autorelease];
[[NSBundle mainBundle] loadNibNamed:#"ModalVC" owner:loader options:nil];
[self presentModalViewController:loader.navVC animated:YES];
Delegate has nothing to do with your issue.
You probably did put navigation bar into your view directly. Thus things like self.navigationItem doesn't work. You have two choices ...
Connect your buttons to outlets in your code and access them directly.
Or remove navigation bar from your view and present your view controller in this way ...
MyViewController *vc = [[MyViewController alloc] initWith...];
UINavigationController *navCon = [[UINavigationController alloc] initWithRootViewController:vc];
[vc release];
[self presentModalViewController:navCon animated:YES];
[navCon release];
... and now you can access left/right buttons via navigationItem.
How to install a NavigationController as the root view in a tab bar view?
In my applicaton:didFinishLaunchingWithOptions: method I created a tab bar interface, setting the rootViewController of the window as the tabBarController.
Now, in one of my tab bar views, I want to add a navigation bar at the top. How can I accomplish this?
Should I subclass navigationcontroller?
Thanks
It sounds like you're doing it in code, not IB, so here's what you can do.
// First create your RootViewController:
UIViewController *rootViewController = [[UIViewController alloc] init];
// Then add the rootViewController to a UINavigationController
UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:rootViewController];
// Now your RootViewController is a UINavigationController
// Add it to your UITabBarController
[tabBarController.viewControllers addObject:navigationController];
// You can now get rid of the RootViewController and UINavigationController
[rootViewController release];
[navigationController release];
You can do this in the Interface Builder. Replace the view controller inside the tab bar controller with a navigation controller. Then set the class and Nib name of the view controller (inside the navigation controller) to your root class.
You have to first create a UITabbarController application, then go to MainWindow.xib file. By default two tab view will be created.
Check the attribute property of the tab bar and change the view to RootViewController. You will have to set the class name and xib file name to RootViewController.
I am showing a modal view which is a UITableViewController class. For some reason it won't show the navigation bar when I show it. Here is my code:
SettingsCreateAccount *detailViewController = [[SettingsCreateAccount alloc] initWithStyle:UITableViewStyleGrouped];
detailViewController.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
detailViewController.navigationController.navigationBarHidden = NO;
[self.navigationController presentModalViewController:detailViewController animated:YES];
detailViewController = nil;
[detailViewController release];
I thought it was shown by default? If it helps, I am calling this from another class that is also a UITableViewController managed by a UINavigationController. Ideas?
When you present a modal view controller it does not use any existing navigation controllers or navigation bars. If all you want is to display a navigation bar, you need to add the navigation bar as a subview of your modal view and present it as you're doing.
If you want to present a modal view controller with navigation functionality, you need to present a modal navigation controller containing your detail view controller instead, like so:
SettingsCreateAccount *detailViewController = [[SettingsCreateAccount alloc] initWithStyle:UITableViewStyleGrouped];
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:detailViewController];
[detailViewController release];
navController.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[self presentModalViewController:navController animated:YES];
[navController release];
Your modal controller will manage its own navigation stack.
Here is one way to display navigation bar for those who are using storyboards, suggested by Apple's Tutorial on Storyboard.
Because a modal view controller doesn’t get added to the navigation stack, it doesn’t get a navigation bar from the table view controller’s navigation controller. To give the view controller a navigation bar when presented modally, embed it in its own navigation controller.
In the outline view, select View Controller.
With the view controller selected, choose Editor > Embed In > Navigation Controller.
On iOS 7 and you just want a navigation bar on your modal view controller to show a title and some buttons? Try this magic in your UITableViewController:
// in the .h
#property (strong) UINavigationBar* navigationBar;
//in the .m
- (void)viewDidLoad {
[super viewDidLoad];
self.navigationItem.title = #"Awesome";
self.navigationBar = [[UINavigationBar alloc] initWithFrame:CGRectZero];
[self.view addSubview:_navigationBar];
[self.navigationBar pushNavigationItem:self.navigationItem animated:NO];
}
-(void)layoutNavigationBar{
self.navigationBar.frame = CGRectMake(0, self.tableView.contentOffset.y, self.tableView.frame.size.width, self.topLayoutGuide.length + 44);
self.tableView.contentInset = UIEdgeInsetsMake(self.navigationBar.frame.size.height, 0, 0, 0);
}
-(void)scrollViewDidScroll:(UIScrollView *)scrollView{
//no need to call super
[self layoutNavigationBar];
}
-(void)viewDidLayoutSubviews{
[super viewDidLayoutSubviews];
[self layoutNavigationBar];
}
I want to share how the accepted solution can be used in projects with storyboards:
The simple approach is to put in a storyboard blank navigation controller before the VC which is to be presented modally, so the relations look like:
(Presenter VC) -> presents modally -> (navigation controller having a controller to be presented as its root).
We've tried this approach for a while and noticed that our storyboards become "polluted" by a large number of such intermediate navigation controllers when each! of them is used exclusively for one! presentation of some other controller, that we want to be presented modally with navigation bar.
Our current solution is to encapsulate the code from accepted answer to a custom segue:
#import "ModalPresentationWithNavigationBarSegue.h"
#implementation ModalPresentationWithNavigationBarSegue
- (void)perform {
UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:self.destinationViewController];
[self.sourceViewController presentViewController:navigationController animated:YES completion:nil];
}
#end
Having this segue in our project we do not create intermediate navigation controllers in our storyboards anymore, we just use this ModalPresentationWithNavigationBarSegue like:
Presenter VC --> Presentee VC
I hope that this answer will be helpful to people who like to avoid unnecessary duplication in their apps storyboards.
I just wanted to add something to what #Scott said. His answer is definitely the easiest and most accepted way of doing it now with Storyboards, iOS 7 and 8... (and soon, 9).
Definitely adding a view controller to the Storyboard and Embedding it as described by #Scott is the right way to go.
Then, just add the segue by control-dragging from the source view controller to the target (the one you want to show modally), select "Present Modally" when the little view appears with the choices for the type of segue. Probably good to give it a name too (in the example below I use "presentMyModalViewController").
One thing that I needed that was missing is #Scott's case is when you want to actually pass on some data to that modally-presented view controller that is embedded in the navigation controller.
If you grab the segue.destinationViewController, it will be a UINavigationController, not the controller you embedded in the UINavigationController.
So, to get at the embedded view controller inside the navigation controller, here's what I did:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"presentMyModalViewController"]) {
// This could be collapsed, but it's a little easier to see
// what's going on written out this way.
// First get the destination view controller, which will be a UINavigationController
UINavigationController *nvc = (UINavigationController *)segue.destinationViewController;
// To get the view controller we're interested in, grab the navigation controller's "topViewController" property
MyModalViewController *vc = (EmailReceiptViewController *)[nvc topViewController];
// Now that we have the reference to our view controller, we can set its properties here:
vc.myAwesomeProperty = #"awesome!";
}
}
Hope this helps!
If you only need a NavigationBar, you can add an instance of UINavigationBar and assign BarItems to it.
I'm trying to essentially re-implement the UISplitViewController (because it has its limits), but when I create a UIViewController viewController, and then do an "[viewController.view addSubview contentViewController.view]" on it, to add a view that already has a view controller, that content view doesn't seem to get initialised by its view controller. I guess its view controller is getting detached or deallocated, is this the case?
Can you post your code?
UIViewController* myController = [[UIViewController alloc] initWithNibName:#"myView" bundle:nil];
myViewClass* cellView = (myViewClass*)cellController.view;
[self addSubview:cellView];
The above code will add a subview using the view in the "myView" nib.
Ensure that in the nib file -
The view is of myViewClass
the File's Owner is UIViewController and
its view outlet is connected to the view.