navigation bar in Detail side of master-detail controller missing - ios

I made masterdetailview with different viewcontrollers in my storyboard and linked every one with push segue. Now when I click on list item new DetailViewController opens, but every single one have no navigationbar. I added navigation item on top off every View, added title for every single one, but after all that navigation bar is not showing. I am working on iPad app and in both orientations navigation bar is missing.
EDIT:
My appDelegate is same as template when you create your Master-Detail project:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
UISplitViewController *splitViewController = (UISplitViewController *)self.window.rootViewController;
UINavigationController *navigationController = [splitViewController.viewControllers lastObject];
splitViewController.delegate = (id)navigationController.topViewController;
}
return YES;
}
One of my DetailViewControllers:
#import "MediaExpDetailViewController.h"
#interface MediaExpDetailViewController ()
#property (strong, nonatomic) UIPopoverController *masterPopoverController;
#end
#implementation MediaExpDetailViewController
#pragma mark - Managing the detail item
- (void)setDetailItem:(id)newDetailItem
{
if (_detailItem != newDetailItem) {
_detailItem = newDetailItem;
}
if (self.masterPopoverController != nil) {
[self.masterPopoverController dismissPopoverAnimated:YES];
}
}
- (void)viewDidLoad{
[super viewDidLoad];
}
#pragma mark - Split view
- (void)splitViewController:(UISplitViewController *)splitController willHideViewController:(UIViewController *)viewController withBarButtonItem:(UIBarButtonItem *)barButtonItem forPopoverController:(UIPopoverController *)popoverController
{
barButtonItem.title = NSLocalizedString(#"MasterButton", #"Master");
[self.navigationItem setLeftBarButtonItem:barButtonItem animated:YES];
self.masterPopoverController = popoverController;
}
- (void)splitViewController:(UISplitViewController *)splitController willShowViewController:(UIViewController *)viewController invalidatingBarButtonItem:(UIBarButtonItem *)barButtonItem
{
// Called when the view is shown again in the split view, invalidating the button and popover controller.
[self.navigationItem setLeftBarButtonItem:nil animated:YES];
self.masterPopoverController = nil;
}
#end

Your DetailViewController has to be connected to a UINavigationController. If you set up a new master-detail-project from Xcode's "New Project" menu you will get this:
Notice: Every Detail view controller has to be connected to a UINavigationController (directly or indirectly).
Edit: To connect multiple UINavigationControllers, do the following:
Your initial detail views UINavigationController is connected with the split view (as usual). Every others detail views UINavigationController is connected with the master view (with a replace segue):
If you want to give the user the possibility to switch back to the initial detail view, you also have to connect the initial view controller with the master (thus having two connections, one to the split, one to the master).
To switch between detail view controllers, you call performSegueWithIdentifier:sender: in your master view controller.
Edit 2:
To add your detail views as the split view's delegate implement the following method, which gets called earlier than viewDidLoad
- (void)awakeFromNib
{
self.splitViewController.delegate = self;
}

Related

Dismiss all Detail Views when switching Tabs on TabBarController

My iOS app has:
TabBarController
NavigationController1
TableView1
ViewController1 (Details View)
NavigationController2
TableView2
ViewController2 (Details View)
Behavior:
When the app loads, I see the TableView1.
I select an Item in the table, and it takes me via Show (Push) segue the details view 1.
I switch to the second tab on the bottom, and see TableView2.
I select an item and it takes me to details view 2
I navigate back to first tab, and see details view 1
Desired:
When performing last step, I'd like to dismiss the details view and see the first TableView1, and when switching back to second tab, I want that one to be dismissed and to see the table view.
I've tried different combinations of dismissViewControllerAnimated and popToRootViewControllerAnimated but I just don't seem to figure it out.
MainTabBarController.h
#interface MainTabBarController : UITabBarController <UITabBarControllerDelegate>
MainTabBarController.m
- (void)viewDidLoad {
[super viewDidLoad];
self.delegate = self;
}
...
- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController{
// NSLog Works fine, and displays information in the output
NSLog (#"%# %lu", tabBarController.selectedViewController.title, tabBarController.selectedIndex);
// None of the lines below achieve the desired result
[viewController.navigationController popToRootViewControllerAnimated:YES];
[viewController dismissViewControllerAnimated:YES completion:nil];
[tabBarController.navigationController popToRootViewControllerAnimated:YES];
[tabBarController dismissViewControllerAnimated:YES completion:nil];
}
One option is to make use of the UITabBarControllerDelegate. Listen for changes to the tab selection. Based on the new tab, get the tab's navigation controller and call its popToRootViewControllerAnimated: method.
Update based on the code added to the question:
The problem is with how you try to pop the view controllers. You want this:
- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController{
// NSLog Works fine, and displays information in the output
NSLog (#"%# %lu", tabBarController.selectedViewController.title, tabBarController.selectedIndex);
// If the selected tab's root controller is a navigation controller
// pop to the top view controller in the tab's navigation stack
if ([viewController isKindOfClass:[UINavigationController class]]) {
UINavigationController *nav = (UINavigationController *)viewController;
[nav popToRootViewControllerAnimated:NO];
}
}
Here is a simple solution for this.
Try to implement the following methods of UIViewContorller
- (void)viewWillDisappear:(BOOL)animated; // Called when the view is dismissed, covered or otherwise hidden. Default does nothing
- (void)viewDidDisappear:(BOOL)animated; // Called after the view was dismissed, covered or otherwise hidden. Default does nothing
Go to your detail-1 view controller and implement the method - (void)viewWillDisappear:(BOOL)animated.
Do a pop for that controller.
Same you should do for the detail-2
Here is the code snippet that will help you.
In Appdelegate.m
#interface AppDelegate ()<UITabBarControllerDelegate>
#property(nonatomic, strong) MainTabBarController *rootTabBarController;
#end
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.rootTabBarController = [[MainTabBarController alloc]init];
self.rootTabBarController.delegate = self;
self.window.rootViewController = self.rootTabBarController;
[self.window makeKeyAndVisible];
}
TabBarController delegate implementation
-(void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController {
NSUInteger index = [self.rootTabBarController.viewControllers indexOfObject:viewController];
NSLog(#"Index : %lu", (unsigned long)index);
switch (index) {
case 0:
// pop other tab barcontrollers pushed or modal windows
[self.rootTabBarController flushViewControllerStackForIndex:1];
break;
case 1:
[self.rootTabBarController flushViewControllerStackForIndex:0];
break;
default:
break;
}
}
MainTabBarController.m
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
[self setViewControllers:#[
[[UINavigationController alloc] initWithRootViewController:[[FirstViewController alloc]init]],
[[UINavigationController alloc] initWithRootViewController:[[SecondViewController alloc]init]]
] animated:YES];
}
-(void)flushViewControllerStackForIndex:(NSUInteger )index {
[[self.viewControllers objectAtIndex:index] popToRootViewControllerAnimated:NO];
}
Here is screenshot in sequence for the sample I ran.
Here is the Sample code.
That should solve your purpose & is the right approach.
Now you may need to fine tune your own logic in flushViewControllerStackForIndex to check if there is just only controller being pushed on stack or a combination of push & modal. So better try to navigate on the Stack & do-a-dismiss-if-a-modal or do-a-pop-if-a-push.
Hope that helps.
You can directly set the view controllers currently on the navigation stack. All you have to is directly set the viewControllers property of the navigation controllers when switch tabs in the tabbar controller.
Set NavigationController1.viewcontrollers = #[tableView1] when you switch to tab1

Having the hidden Master View Controller display the first time the app is run

I have configured a UISplitViewController on the iPad iteration of my app where in portrait mode, there's a UIBarButtonItem which calls out the Master View. In landscape, this view is displayed always (both Master and Detail View).
Because the iPad version is new for my users, and because they're used to seeing a Tab Bar on the iPhone, I want to make sure the users are alerted to where the menu has gone.
I want my Master View to be displayed the very first time the user launches the app.
So when the user presses the UIBarButtonItem, the Master view is displayed, but the very first time the app is launched, I want to have the Master View displayed (i.e. the UIButton BarButtonItem pressed).
I'm familiar with the process of checking when the app has been launched for the first time. I just need to know how to get the button to be called.
I have this code in the Detail:
#pragma mark - Split View Handler
-(void) turnSplitViewButtonOn: (UIBarButtonItem *)barButtonItem forPopoverController:(UIPopoverController *) popoverController {
barButtonItem.title = NSLocalizedString(#"Master", #"Master");
_splitViewBarButtonItem = barButtonItem;
[self.navigationItem setLeftBarButtonItem:barButtonItem animated:YES];
self.masterPopoverController = (EnvylopeMasterTableViewController *)popoverController;
}
-(void)turnSplitViewButtonOff
{
NSLog(#"SplitViewButtonOff Called");
// Called when the view is shown again in the split view, invalidating the button and popover controller.
[self.navigationItem setLeftBarButtonItem:nil animated:YES];
_splitViewBarButtonItem = nil;
self.masterPopoverController = nil;
}
-(void) setSplitViewButton:(UIBarButtonItem *)splitViewButton forPopoverController:(UIPopoverController *)popoverController
{
NSLog(#"Split View Being Called");
if (splitViewButton != _splitViewBarButtonItem) {
if (splitViewButton) {
NSLog(#"Split View Button Being Called");
[self turnSplitViewButtonOn:splitViewButton forPopoverController:popoverController];
} else {
[self turnSplitViewButtonOff];
NSLog(#"Split View Button Not Being Called");
}
}
}
This is my code in the Master:
#pragma mark - Split View Delegate
- (void)splitViewController:(UISplitViewController *)splitController willHideViewController:(UIViewController *)viewController withBarButtonItem:(UIBarButtonItem *)barButtonItem forPopoverController:(UIPopoverController *)popoverController
{
UINavigationController *navController = [[[self splitViewController ] viewControllers ] lastObject ];
id vc = [[navController viewControllers] firstObject];
self.popover = popoverController;
[vc setSplitViewButton:barButtonItem forPopoverController:popoverController];
}
- (void)splitViewController:(UISplitViewController *)splitController willShowViewController:(UIViewController *)viewController invalidatingBarButtonItem:(UIBarButtonItem *)barButtonItem
{
UINavigationController *navController = [[[self splitViewController ] viewControllers ] lastObject ];
id vc = [[navController viewControllers] firstObject];
self.popover = nil;
[vc setSplitViewButton:nil forPopoverController:nil];
}
When the app is launched, the NSLog "Split View Being Called" and the "Split View Button Being Called" is being output to the console, but the button has not been pressed and the Master View is not being displayed.
I hope this makes sense and if anyone has any guidance on this, that would really be appreciated.
I think that a decent approach would be to utilize NSUserDefaults to check and see if it's the users' first launch.
You can reference this post to learn how you can do this.
Once you have that value stored it'd be as simple as conditionally checking it upon app launch and doing your 'first app launch' setup if it is indeed their first launch.
edit:
You could programmatically touch the button once the app opens based around whether it's the users first time or not. Check out this.

iPad Split View Not Showing BarButtonItem When Switching View Controllers in Master View Controller

I am quite new to iPad Development, so please forgive my questions that might seem a little obvious.
I have an existing application for the iPhone which I am converting to a universal app for the iPad version.
The UI is going to be simple.
A Split View Controller where the Master represents a Static UITableView (Person, Timeline, Event, Date) for the user to select. The Detail will of course display whichever cell was selected in the UITableView Master. Both the Detail and Master have been created in Storyboard and are both NavigationControllers.
I am trying to do the common aspect of: Landscape mode shows both the Master and Detail View, while Portrait shows only the Detail View, but with a UIBarButtonItem that pops out the Master when requested.
Issue
When I rotate from Landscape to Portrait, the UIBarButtonItem is visible and I can press it to bring out the Master View in Portrait mode.
In portrait mode, if I bring out the button and select a different cell from the Master View, when I dismiss the Master by tapping anywhere in the Detail View, it doesn't now show the UIBarButton.
I am following http://www.dharmaworks.net/Consulting/switching-detail-views-in-uisplitviewcontroller-with-ios7/ as a way to get try get this working.
Code
MasterTableView:
#pragma mark - Split View Delegate
- (void)splitViewController:(UISplitViewController *)splitController willHideViewController:(UIViewController *)viewController withBarButtonItem:(UIBarButtonItem *)barButtonItem forPopoverController:(UIPopoverController *)popoverController
{
UINavigationController *navController = [[[self splitViewController ] viewControllers ] lastObject ];
id vc = [[navController viewControllers] firstObject];
[vc setSplitViewButton:barButtonItem forPopoverController:popoverController];
}
- (void)splitViewController:(UISplitViewController *)splitController willShowViewController:(UIViewController *)viewController invalidatingBarButtonItem:(UIBarButtonItem *)barButtonItem
{
UINavigationController *navController = [[[self splitViewController ] viewControllers ] lastObject ];
id vc = [[navController viewControllers] firstObject];
[vc setSplitViewButton:nil forPopoverController:nil];
}
SplitViewPresenter Protocol
#property (nonatomic, strong) UIBarButtonItem *splitViewBarButtonItem;
-(void)setSplitViewButton:(UIBarButtonItem *)splitViewButton forPopoverController:(UIPopoverController *)popoverController;
Detail View
-(void) turnSplitViewButtonOn: (UIBarButtonItem *)barButtonItem forPopoverController:(UIPopoverController *) popoverController {
barButtonItem.title = NSLocalizedString(#"Master", #"Master");
_splitViewBarButtonItem = barButtonItem;
[self.navigationItem setLeftBarButtonItem:barButtonItem animated:YES];
self.masterPopoverController = (MasterTableViewController *)popoverController;
}
-(void)turnSplitViewButtonOff
{
NSLog(#"SplitViewButtonOff Called");
// Called when the view is shown again in the split view, invalidating the button and popover controller.
[self.navigationItem setLeftBarButtonItem:nil animated:YES];
_splitViewBarButtonItem = nil;
self.masterPopoverController = nil;
}
-(void) setSplitViewButton:(UIBarButtonItem *)splitViewButton forPopoverController:(UIPopoverController *)popoverController
{
NSLog(#"Split View Being Called");
if (splitViewButton != _splitViewBarButtonItem) {
if (splitViewButton) {
NSLog(#"Split View Button Being Called");
[self turnSplitViewButtonOn:splitViewButton forPopoverController:popoverController];
} else {
[self turnSplitViewButtonOff];
NSLog(#"Split View Button Not Being Called");
}
}
}
The .h file of the Detail View is declared to use the SplitViewPresenter.
Update
I understand why this is occurring. The setter is only getting called when I rotate from the Landscape to Portrait mode. My question is, how do I get this to work without the rotation? Also, within the link above that I have followed, I have not implemented any of the didSelectRow code. Should I be doing that?
Any guidance on this would really be appreciated.

Hide and Unhide masterView from button press of detailView

I know this question has asked by many users,But i did not found any answer related to my issue.
I'm using UISplitViewController ,my application starts with login page, so i have hidden masterView on start, after some time on DetailViewController i shown the master viewController using Delegate method.
- (BOOL)splitViewController:(UISplitViewController *)svc shouldHideViewController:(UIViewController *)vc inOrientation:(UIInterfaceOrientation)orientation NS_AVAILABLE_IOS(5_0)
{
return hideMaster;
}
i have initialised hideMaster to NO ,and my ViewDidLoad() of DetailViewController is below
ViewDidLoad(){
[self.splitViewController.view setNeedsLayout];
self.splitViewController.delegate =Nil;
self.splitViewController.delegate = self;
[self.splitViewController willRotateToInterfaceOrientation:[UIApplication sharedApplication].statusBarOrientation duration:0];
[super viewDidLoad];
}
it worked fine my master is now unhide.But on the same detailViewController i have a Back Button on which i'm poping the current ViewController to last viewController and again i want to hide masterViewController my code for back button is below.
- (IBAction)back:(id)sender {
hideMaster = NO;
self.splitViewController.delegate =Nil;
self.splitViewController.delegate = self;
[self.splitViewController willRotateToInterfaceOrientation:[UIApplication sharedApplication].statusBarOrientation duration:0];
[self.navigationController popViewControllerAnimated:NO];
}
Its poping the CurrentViewController to last ViewController but its not Hiding the masterViewController
Please help me out.
#JohnD,I went through your code.you are hiding the master view controller while poping the last View controller,but the viewController to which you are navigating is still showing the master View controller.therefore your masterView is still there.
follow the steps given below.
1.make delegate of UInavigationController to that ViewController which is your last singleView controller.
2.In last single ViewController(which is delegate of UINavigationController) implement following delegate method.
- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated
{
[self.splitViewController.view setNeedsLayout];
self.splitViewController.delegate =Nil;
self.splitViewController.delegate = self;
[self.splitViewController willRotateToInterfaceOrientation:[UIApplication sharedApplication].statusBarOrientation duration:0];
}
3.Now implement delegate method of UISplitViewController in side lastViewController which is given below.
- (BOOL)splitViewController:(UISplitViewController *)svc shouldHideViewController:(UIViewController *)vc inOrientation:(UIInterfaceOrientation)orientation NS_AVAILABLE_IOS(5_0)
{
return hideMaster;<<===============I'm assuming hide master as a variable of shared object(Singleton).
}
4.Now change your - (IBAction)back:(id)sender method with following one.
- (IBAction)back:(id)sender {
hideMaster = Yes;
[self.navigationController popViewControllerAnimated:NO];
}
I hope this will work,if you stuck some where please let me know.
you are using UISplitViewController inside your app,since you have many views but whole app is not using UISplitViewController only one view using it.
Its better to use another approach.

UISplitViewController in UITabBarController in iOS 5

I have a tabbar with splitviewcontroller on every item. I used a subclass from that site https://github.com/grgcombs/IntelligentSplitViewController and in iOS 4 every worked fine, but in iOS 5 I have a problem. When I start the app in portrait mode, then change to landscape and go to the second item in tabbar, then again change to portrait mode and click the popover, then dismiss the popover, go to the first item in tabbar, again open popover and change to the landscape, the master view disappears and popover is still visible. Has anyone any idea how to fix it?
I have been working on solving the same problem. Instead of using the UISplitViewController subclass you indicate, I created a UITabBarController subclass which properly propagates the rotation messages to all UISplitViewControllers it contains. This maintains the correct internal state of the UISplitViewControllers. However, one of the SplitViewController delegate methods is not called if the SplitViewController is not visible, so I handle this in the detail view controller. I've confirmed this works in iOS5.0 - iOS6.1
OSTabBarController.m
#import "OSTabBarController.h"
#implementation OSTabBarController
-(void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration{
[super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];
for(UIViewController *targetController in self.viewControllers){
if(targetController != self.selectedViewController && [targetController isKindOfClass:[UISplitViewController class]]){
[targetController willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];
}
}
}
-(void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation{
[super didRotateFromInterfaceOrientation:fromInterfaceOrientation];
for(UIViewController *targetController in self.viewControllers){
if(targetController != self.selectedViewController && [targetController isKindOfClass:[UISplitViewController class]]){
[targetController didRotateFromInterfaceOrientation:fromInterfaceOrientation];
}
}
}
#end
DetailViewController
#implementation OSDetailViewController
-(void)viewWillAppear:(BOOL)animated{
//the splitViewController:willHideViewController:withBarButtonItem:forPopoverController: may not have been called
if(!UIInterfaceOrientationIsPortrait(self.interfaceOrientation)){
self.navigationItem.leftBarButtonItem = nil;
}
}
#pragma mark - UISplitViewControllerDelegate Methods
- (void)splitViewController:(UISplitViewController *)splitController willHideViewController:(UIViewController *)viewController withBarButtonItem:(UIBarButtonItem *)barButtonItem forPopoverController:(UIPopoverController *)popoverController
{
[self.navigationItem setLeftBarButtonItem:barButtonItem animated:YES];
}
- (void)splitViewController:(UISplitViewController *)splitController willShowViewController:(UIViewController *)viewController invalidatingBarButtonItem:(UIBarButtonItem *)barButtonItem
{
[self.navigationItem setLeftBarButtonItem:nil animated:YES];
}
#end

Resources