I'd like to launch a modal view controller the way one does with 'ABPeoplePickerNavigationController' and that is without having to creating a navigation controller containing the view controller.
Doing something similar yields a blank screen with no title for the navigation bar and there's no associated nib file loaded for the view even though I am invoking the initWithNibName when the 'init' is called.
My controller looks like:
#interface MyViewController : UINavigationController
#implementation MyViewController
- (id)init {
NSLog(#"MyViewController init invoked");
if (self = [super initWithNibName:#"DetailView" bundle:nil]) {
self.title = #"All Things";
}
return self;
}
- (void)viewDidLoad {
[super viewDidLoad];
self.title = #"All Things - 2";
}
#end
When using the AB controller, all you do is:
ABPeoplePickerNavigationController *picker = [[ABPeoplePickerNavigationController alloc] init];
picker.peoplePickerDelegate = self;
[self presentModalViewController:picker animated:YES];
[picker release];
ABPeoplePickerNavigationController is declared as:
#interface ABPeoplePickerNavigationController : UINavigationController
The other way to create a modal view as suggested in Apple's 'View Controller Programming Guide for
iPhone OS':
// Create a regular view controller.
MyViewController *modalViewController = [[[MyViewController alloc] initWithNibName:nil bundle:nil] autorelease];
// Create a navigation controller containing the view controller.
UINavigationController *secondNavigationController = [[UINavigationController alloc] initWithRootViewController:modalViewController];
// Present the navigation controller as a modal view controller on top of an existing navigation controller
[self presentModalViewController:secondNavigationController animated:YES];
I can create it this way fine (as long as I change the MyViewController to inherit from UIViewController instead of UINavigationController). What else should I be doing to MyViewController to launch the same way as ABPeoplePickerNavigationController?
I'd like to launch a modal view controller the way one does with 'ABPeoplePickerNavigationController' and that is without having to creating a navigation controller containing the view controller
But this is exactly what ABPeoplePickerNavigationController is doing. It isn't magic, it is a UINavigationController that instantiates a UIViewController internally (a UITableView that is populated with your address book contacts) and sets the UIViewController as its root view.
You can indeed create your own similar UINavigationcontroller subclass. However, within it's initializer, you will need to create a view controller to load as its root view just like ABPeoplePickerNavigationController does.
Then you can do what you are trying like this:
[self presentModalViewController:myCutsomNavigationController animated:YES];
In the code you posted:
#interface MyViewController : UINavigationController
#implementation MyViewController
- (id)init {
NSLog(#"MyViewController init invoked");
if (self = [super initWithNibName:#"DetailView" bundle:nil]) {
self.title = #"All Things";
}
return self;
}
- (void)viewDidLoad {
[super viewDidLoad];
self.title = #"All Things - 2";
}
#end
I suspect you are having NIB issues. there isn't a "rootViewController" outlet to connect. This is why you have a blank screen.
The initalizer you should be using internally is this:
self = [super initWithRootViewController:myCustomRootViewController];
Related
I initiate the following Modal Controller:
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
UIViewController *modal = [storyboard instantiateViewControllerWithIdentifier:#"modalController"];
modal.title = #"Example Title";
[self presentModalViewController:modal animated:YES];
I set the title with:
modal.title = #"Example Title";
but this doesn´t work, can anyone help me?
Edit:
I have wrapped my ModalView with a UINavigationController like this
You are going about this in a slightly convoluted way... but to stay with your paradigm, you need to present the navigationController, not the contained viewController: trying to do it the latter way will instantiate the viewController, but this action will not pull the containing navController along with it out of the storyboard. You are setting the viewController's title property ok, but you have no (automated) way to display the title. Whereas if you instantiate the navController, it's contained viewController does get unarchived along with it as it's topViewController.
//give the navigation controller a storyboard id eg "navVC"
UINavigationController* modalNav = [self.storyboard instantiateViewControllerWithIdentifier:#"navVC"];
[modalNav topViewController].title = #"Example Title";
//[self presentModalViewController:modalNav animated:YES];
//deprecated method, use this instead:
[self presentViewController:modalNavController
animated:YES
completion:nil];
You have to add a IBOutlet property for UINavigationItem (the title) in your model controller with a classic drag & drop method.
#property (weak, nonatomic) IBOutlet UINavigationItem *navTitle;
Then set the title in viewDidLoad function.
- (void)viewDidLoad
{
[super viewDidLoad];
// title view
[self.navTitle setTitle: #"atitle"];
}
I first like to load simple viewController which shows some option and then clicking on some button I would like to load navigationController or tabbarController depending on button click. How can I do this ?
I replace the root view controller on the window when I want to switch the views.
For example in my app I show a loading screen first then I switch the view to a login screen.
To do this you need a reference to your app delegate then you can access the window property and replace the root view controller:
AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
LoginViewController *loginVC = [[LoginViewController alloc] init];
appDelegate.window.rootViewController = loginVC;
In your simpleViewController :
- (IBAction) yourButtonAction:(id)sender
{
UIViewController *Vc = [[theViewControllerYouWantToShow alloc]init];
UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:Vc];
[self presentModalViewController:nav animated:YES];
}
Edit :
you have three options to show your viewController content :
as the example above using presentModalViewController:
add the viewController view as a subView to the current viewController.
in your case : [simpleViewController.view addSubView:nav.view];
3.or if your simple ViewController is the navigation root viewController you can push other viewControllers to its navigation stack.
in appdelegate.h
#property (strong, nonatomic) id<UIApplicationDelegate>delegate;
in appdelgate.m
#synthesize delegate;
in my first viewController's .h file
AppDelegate *myappDelegate;
-(IBAction)start:(id)sender;
in my first viewController's .m file
-(IBAction)start:(id)sender
{
NSLog(#"Start Button is clicked");
mvc = [[MasterViewController alloc]initWithNibName:#"MasterViewController" bundle:nil];
myappDelegate = [[UIApplication sharedApplication]delegate];
myappDelegate.navigationController = [[UINavigationController alloc]initWithRootViewController:mvc];
myappDelegate.window.rootViewController = myappDelegate.navigationController;
[myappDelegate.window makeKeyAndVisible];
}
My controller hierachy:
TabBaseController (UITabBarController)
SubclassedController
In my tabbasecontroller I have a navigation bar button, which flips the subclassedcontroller with the presentModalViewController method, to a second UITabBarController.
So my question is: why does not
self.parentViewController
work in the second UITabBarController? It is nil.
I am trying this in my viewDidLoad method in the second UITabBarController:
if (self.parentViewController == nil) {
NSLog(#"Parent is nil");
}
UPDATED
This is the method in the UITabBarController with the navigationItemButton that presents it
-(IBAction)openModalTabController:(id)sender {
if (self.nvc == nil) {
ModalTabController *vc = [[ModalTabController alloc] init];
self.nvc = vc;
[vc release];
}
[self presentModalViewController:self.nvc animated:YES];
}
This is the controller(UITabBarController) that I present modally:
Header:
#interface NewBuildingViewController : UITabBarController {
}
#end
Main:
#implementation NewBuildingViewController
- (id)init {
[super initWithNibName:nil bundle:nil];
ViewController1 *vc1 = [[ViewController1 alloc] init];
ViewController2 *vc2 = [[ViewController2 alloc] init];
ViewController3 *vc3 = [[ViewController3 alloc] init];
NSArray *controllers = [[NSArray alloc] initWithObjects:vc1, vc2, vc3, nil];
[vc1 release];
[vc2 release];
[vc3 release];
self.viewControllers = controllers;
[controllers release];
self.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
return self;
}
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
return [self init];
}
#end
I also want to add that this message is displayed in the console(warning) when flipping:
Using two-stage rotation animation. To use the smoother single-stage animation, this application must remove two-stage method implementations.
Using two-stage rotation animation is not supported when rotating more than one view controller or view controllers not the window delegate
It would be helpful if you were to show how you are presenting that second UITabBarController. Are you perhaps ignoring the following warning found in the UITabBarController class reference?
When deploying a tab bar interface, you must install this view as the root of your window. Unlike other view controllers, a tab bar interface should never be installed as a child of another view controller.
I want to use a modal view (UIViewController) as a "normal" view, which can be pushed on the navigation controller stack. Normally, a modal view is presented like this:
LoginViewController *myView = [[MyViewController alloc] init];
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:myView];
[self.navigationController presentModalViewController:navController animated:YES];
[myView release];
myView = nil;
[navController release];
navController = nil;
But I want to do something like this:
[[self navigationController] pushViewController:myView animated:YES];
The problem is that my modal view has a right and a left button. So I would have to check how the view is loaded and present the buttons in another way. The idea behind this is to have the back button. So I can use the same modal view a few times.
Edit:
#petert:
Now I followed your example. My issue is that I'm using a UINavigationBar for the modal view. To get this UINavigationBar I create a navigation controller. I'm using the navigation bar because I have my buttons in it. So checking if parentViewController is of type UINavigationController does not work for me. I'm always getting a modal view. Here is how I do it:
// load modal view
MyViewController *myView = [[MyViewController alloc] init];
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:myView];
[[self navigationController] presentModalViewController:navController animated:YES];
[navController release];
navController = nil;
[myView release];
myView = nil;
// load as normal view
MyViewController *myView = [[MyViewController alloc] init];
[[self navigationController] pushViewController:myView animated:YES];
Good tips in this StackOverflow answer.
I prefer to use UIViewController's property:
#property(nonatomic, readonly) UIViewController *parentViewController
in a view controller's subclass:
Look at the value of the controller's parentViewController property. If it's an instance of UINavigationController, then you're in the navigation stack. If you're being displayed modally, it'll be an instance of your last view controller.
So in -viewDidLoad for example:
- (void)viewDidLoad
{
if ([self.parentViewController isKindOfClass:[UINavigationController class]])
{
// navigation controller
self.title = #"...";
}
else
{
// modal
self.title = #"Modal";
// add cancel and done buttons now...
}
}
Or, a pretty simple solution would be to customize your init method to your MyViewController class to encode your intent for the view controller.
Add the following to the MyViewController header:
#interface MyViewController : UIViewController
{
BOOL modal;
}
- (id)initForModal:(BOOL)isModal;
#end
Now in the implementation file:
#interface MyViewController ()
#property (nonatomic) BOOL modal;
#end
#implementation MyViewController
#synthesize modal;
- (id)initForModal:(BOOL)isModal;
{
if (self = [super initWithNibName:#"MyViewController" bundle:nil])
{
self.modal = isModal;
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
if (self.modal)
{
// add cancel and done buttons …
}
else
{
// assuming we're presented from a navigation view …
}
}
Now to use this modally:
// load modal view
MyViewController *myView = [[MyViewController alloc] initForModal:YES];
Or not modally:
// load as normal view
MyViewController *myView = [[MyViewController alloc] initForModal:NO];
I'm assuming you're creating the view controller(s) from NIBs, but as always see the View Controller Progamming Guide for iOS and especially the section titled "Defining a Custom View Controller Class".
For clarification: myView isn't modal. You just present it as a modal one.
If you just push it into a UINavigationController hierarchy it will behave like a "normal" one.
You can't push the same view controller onto the navigation stack several times. Just once.
Also see this for how to customize the view:
SO modal question
Hey everyone, I am new to iPhone development and I'm not understanding the whole UINavigationController and UITabBarController idea. Is one a substitute for the other - how do apps such as Tweetie combine both?
I'd like to have my app have a persistent Tab Bar # the bottom (which seems to be working), but also a Navigation bar at the top which can push/pop views onto the screen without removing the tab bar.
How can I accomplish this?
What should the hierarchy look like in IB as far as my MainWindow.xib with regards to all of these controllers?
What is best practice here?
Thanks very much,
Just wrap the view controller inside the UINavigationController and Place the UINavigationController inside the UITabBar.
This will work fine for you…
Example:
NSMutableArray *tabBarViewControllers = [[NSMutableArray alloc] initWithCapacity:2];
tabBarController = [[UITabBarController alloc] init];
[tabBarController setDelegate:self];
UINavigationController *navigationController = nil;
navigationController = [[UINavigationController alloc] initWithRootViewController:<Your View controller1>];
[tabBarViewControllers addObject:navigationController];
[navigationController release];
navigationController = nil;
navigationController = [[UINavigationController alloc] initWithRootViewController:<Your View controller2>];
[tabBarViewControllers addObject:navigationController];
[navigationController release];
navigationController = nil;
tabBarController = tabBarViewControllers;
[tabBarViewControllers release];
tabBarViewControllers = nil;
Use the wizard for a Tab Bar Application, and set it up as normal. In any tab where you want to add a navigation controller, create it in the XIB using the library. My XIB has:
- File's Owner DescriptiveNameNavViewController
- First Responder
- View UIVIew
- Navigation Controller UINavigationController
- Navigation Bar UINavigationBar
Note that there isn't anything in the view. See viewDidLoad below for where the UINavigationController gets attached to the UIView.
In the header file for the Tab's ViewController (which I've here called DescriptiveNameNavViewController -- there isn't a particular standard for this, but I use [Something]NavViewController to remind me that this ViewController contains a navigation controller with the navigation stack. This is the controller name that I set in the MainWindow.xib that the wizard generates) Set up a UINavigationController * IBOutlet that has the navigation controller in the XIB attached to it:
#interface DescriptiveNameNavViewController : UIViewController {
UINavigationController *navigationController;
}
#property (nonatomic, retain) IBOutlet UINavigationController *navigationController;
#end
In the controller for the DescriptiveNameNavViewController , do something like this:
- (void)viewDidLoad {
[super viewDidLoad];
[[self view] addSubview:[navigationController view]];
DescriptiveNameController *aController = [[[DescriptiveNameController alloc ] initWithNibName:#"DescriptiveNameController" bundle:nil ] autorelease];
aController.title = #"Descriptive Title";
//
// app stuff goes here.
//
[self.navigationController pushViewController:aController animated:YES];
[self.navigationController setDelegate:self];
}
Setting the delegate in the DescriptiveNameNavViewController is super-important, because otherwise you won't get the methods called that you expect in DescriptiveNameViewController instances and anything else you push into the navigation controller's stack.
In DescriptiveNameNavViewController, implement the UINavigationControllerDelegate methods like this:
- (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated
{
if ([viewController respondsToSelector:#selector(viewDidAppear:)]) {
[viewController viewDidAppear:animated];
}
}
And that will cause messages to get propagated to controllers inside the UINavigationController like you expect. It seems like many problems that people encounter are because the viewDidAppear: or other methods aren't getting called on the ViewControllers pushed into the NavigationController.
Anyway, let me know if more detail would help.