How to let child view controller modify navigation controller?
I am faced with this problem right now, and have not been able to find a solution that works, let alone one that works well.
Basically, right now I have a UINavigationController that contains a UITableViewController with a search bar in the navigation bar. So far so good - everything works well.
However, I would like to show some custom views between the navigation bar and the top of the table view at times. To do this, I figured it would be best to create another view controller (let's call it the ContainerViewController), which is presented by the navigation controller. This container view controller holds the table view controller as a child, and can insert any custom view it wishes to. The view controller hierarchy should look like this:
UINavigationController
ContainerViewController
UITableViewController (with UISearchDisplayController)
Now I am faced with a problem, since the search bar of the UISearchDisplayController should be displayed in the navigation bar (by calling didSetupSearchDisplayController), but I obviously want all logic pertaining to it kept in the UITableViewController. I need the container view controller to sort of act as a go-between here. I have tried to figure this out, but haven't managed a solution yet.
Here is how I instantiate the table view controller and add it as a child in the container view controller.
- (void)viewDidLoad {
[super viewDidLoad];
// Load view controller from storyboard
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Main" bundle: nil];
self.contentViewController = [storyboard instantiateViewControllerWithIdentifier:#"MyListViewController"];
self.contentViewController.view.translatesAutoresizingMaskIntoConstraints = NO;
// Add its view to the content view
[self addChildViewController:self.contentViewController];
[self.container addSubview:self.contentViewController.view];
[self.contentViewController didMoveToParentViewController:self];
// Set up constraints
[self.contentViewController.view autoPinEdgesToSuperviewEdgesWithInsets:UIEdgeInsetsZero];
}
In the child view controller, I have noticed that self.navigationController is nil in viewDidLoad but not in didMoveToParentViewController. So the following works:
- (void)didMoveToParentViewController:(UIViewController *)parent {
// Hide separator below navigation bar
self.navigationController.navigationBar.clipsToBounds = YES;
}
But I cannot set the search bar to display in the navigation bar, nor can I set the navigation bar title (with self.title = ...) from within the child view controller.
Would appreciate any help.
EDIT: Here is some code I use in the UITableViewController (the contained view) to create the search display controller:
UISearchBar *searchBar = [[UISearchBar alloc] init];
self.mySearchDisplayController = [[UISearchDisplayController alloc] initWithSearchBar:searchBar contentsController:self];
// Show search bar in navigation bar (instead of in its own bar underneath)
self.searchDisplayController.displaysSearchBarInNavigationBar = YES;
// Set self as delegate to search display controller and its table view
self.searchDisplayController.delegate = self;
self.searchDisplayController.searchResultsDelegate = self;
I have tried doing this both in viewDidLoad, where the self.navigationController is nil, and in didMoveToParentViewController, where the navigation controller exists, but it does not seem to matter. My guess is that the displaysSearchBarInNavigationBar magic happens after the child view is instantiated but before it is added as a child view (and thus gets access to the navigation controller).
can you use
self.parentViewController.navigationController
to get the reference to the navigation controller from within the container view?
This may help you find your answer. We use this to set the title of our navigation controller
self.navigationItem.title=#"Name of view goes here";
You should be able to use self.navigationItem to get a handle to the navigation bar.
Related
I am writing an application with help screens. As per my requirement I need to show the help screens when the view appears for the first time and I am placing the code on the tabbar controller class as (void)viewDidLoad:
{
[super viewDidLoad];
XDKWalkthroughController *sc = [[XDKWalkthroughController alloc]
initWithView:self.view];
[self addChildViewController:sc];
[self.view addSubview:sc.view];
sc.delegate = self;
[sc start];
}
When I am placing this code I am getting one more tabbar item along with existing tabbar item. How to avoid that in my case?
And same thing when I placed in the first view i.e in the first tab view controller view did class. Both navigation bar and tabbar are pushing the child view back.
I have tried these two scenarios, please help me with the possible solution.
The root View View controller is Navigation controller and its first level is a TabViewController.One of the tab item is a TableViewController.
Here is the relationship:
However the navigation bar overlap the table view:
I have also set simulated metrics,So what can be the problem??
Thanks for any help.
Simulated metrics are just that, simulated. They do not actually apply to the compiled product.
To fix this, I find it easiest to set the edgesforextendedlayout with the various values of edge values. Usually all but the top.
The rootViewController should be the UITabBarController. Follow this code:
1.Make the UITabBarController the rootViewController in the application delegate or in your main.storyboard set it as the initial View Controller.
2.In the UITabBarController.m place this code there to create the UINavigationController with a UIViewController embeded inside of it.
//Inside UITabBarController.m
- (void)viewDidLoad {
[super viewDidLoad];
UIViewController *vc = [[UIViewController alloc]init];
UINavigationController *navCon = [[UINavigationController alloc] initWithRootViewController:vc];
self.viewControllers = #[navCon];
}
Get rid of the navigation controller in the beginning and, instead, embed the tableviewcontroller inside a navigation controller.
(Select the view controller -- Click "editor" > "embed in" > "navigation controller").
Make sure the tab bar controller is the root view controller as well
This will also fix the overlapping issue
Im having difficulty getting the titles of the navigation bars to display along with the buttons within my tabBarController interface. Im creating the tabBarController programmatically. Here is the screenshot for reference.
I have tried putting self.navigationController.navigationBarHidden = YES; within the alloc/init method of the tabBarController which is allocated in the appDelegate and set as the windows rootViewController. I've also tried to set its title with this code self.navigationController.title = [[self.viewControllers objectAtIndex:self.selectedIndex]title ];. I have also tried using the same code within the viewDidLoad method of my tabBarController class. Within the UITabBarController's alloc/init method I do have this code to set the nav controllers that I have added to the viewControllers array.
UINavigationController *nav2 = [[UINavigationController alloc]initWithRootViewController:contactsTblView];
nav2.title = #"Contacts";
nav2.navigationItem.title = #"Contacts";
nav2.tabBarItem = [[UITabBarItem alloc]initWithTabBarSystemItem:UITabBarSystemItemContacts tag:2];
nav2.delegate = self ;
The title that appears in the navigation bar is the title of the currently showing view controller (the top of the navigation controller's stack). You should set the title of the individual view controllers embedded in the navigation controller, not the navigation controller itself.
I am creating a view controller that is pushed to the UINavigationController. I need a customized navigation bar, therefore I have overridden navigationItem method of my view controller. In a titleView (property of UINavigationItem) I need a custom view that holds two buttons. Question: how to apply view controller for those two buttons?
View containing two buttons is defined in CustomTitleViewController.xib, whereas its view controller is defined in CustomTitleViewController class.
This is my way of returning the navigation item (MainViewController.m):
- (UINavigationItem *)navigationItem
{
UINavigationItem *navItem = [[UINavigationItem alloc] init];
UIViewController *customTitleViewController =
[[CustomTitleViewController alloc] initWithNibName:#"CustomTitleViewController"
bundle:[NSBundle mainBundle]];
navItem.titleView = [customTitleViewController view];
return navItem;
}
The view is shown on the navigation bar, as expected. However, tapping the button crashes the application (EXC_BAD_ACCESS).
Error message: message sent to deallocated instance 0x6e53850.
Any ideas?
Basically, you need to retain pointer to viewController as well as to its view. Just create strong property on your UINavigation controller subclass. Coz what you are doing here is instantiating new controller each time navigation item is called and realising it at the end of the function. Here is a very quick crude fix:
https://www.dropbox.com/s/y6ltdyj951ioncd/Navigating.zip
be sure not to reinstantiate VC all the time and keep pointer to it.
Hope this helped.
I have a navigation controller named navController made programmatically in my modal view controller during its viewDidLoad:
self.navController = [[UINavigationController alloc] initWithRootViewController:self];
self.navController.view=self.view;
[self setView:self.navController.view];
But when i launch the modal view controller i dont see the navigation bar, just the standard view i made in IB. Whats wrong?
Your solution cannot work.
Suppose that you have your modal controller called ModalViewController. It's a simple UIViewController linked with a xib created interface.
Now, at some point you need to present ModalViewController modally. As you wrote in your specification, I think you want to use also a UINavigationController and control its navigation bar.
The code to do this could be the following, where presentModally could be a method that it's not contained in ModalViewController.
- (void)presentModally:(id)sender {
ModalViewController *modalController = [[ModalViewController alloc] initWithNibName:#"ModalView" bundle:nil];
// Create the navigation controller and present it.
UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:modalController];
[self presentViewController:navigationController animated:YES completion: nil];
}
Now, within viewDidLoad of your ModalViewController you have access to navigationController property. In this manner you can control navigationController behaviour. For example:
- (void)viewDidLoad
{
[super viewDidLoad];
// the code changes the title for the navigation bar associated with the UINavigationController
self.title = #"Set from ModalViewController";
}
Some notes
To understand how UINavigationController works read UINavigationController class reference
To understand how modal controllers work read Modal view controllers documentation
The code I provided is a simple example and only demonstrative (I've written by hand so check for syntax). You need to make attention to memory management and how to present modal controllers. In particular, as Apple documentation suggests, to present modal controllers you need to follow these steps:
Create the view controller you want to present.
Set the modalTransitionStyle property of the view controller to the desired value.
Assign a delegate object to the view controller. Typically the delegate is the presenting view controller. The delegate is used by the presented view controllers to notify the presenting view controller when it is ready to be dismissed. It may also communicate other information back to the delegate.
Call the presentViewController:animated:completion: method of the current view controller, passing in the view controller you want to present.
Trigger (when necessary) some action to dismiss the modal controller.
Hope it helps.