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
Related
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.
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.
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;
}
I have an iPad SplitViewController application in which I hide the left pane in both portrait and landscape modes. I need to show the left pane in its popover when a certain notification is received by the application. Despite experimenting with a number of different suggested solutions I am struggling to work out how to achieve this.
I am using a MultipleMasterDetailManager implementation that includes the following code:
/* forward the message to the current detail view
* all detail views must implement UISplitViewControllerDelegate
*/
-(void)splitViewController:(UISplitViewController *)svc willHideViewController:(UIViewController *)aViewController withBarButtonItem:(UIBarButtonItem *)barButtonItem forPopoverController:(UIPopoverController *)pc
{
self.masterBarButtonItem = barButtonItem;
self.masterPopoverController = pc;
barButtonItem.title = NSLocalizedString(#"Show Sidebar", #"Show Sidebar");
[self.currentDetailController.navigationItem setLeftBarButtonItem:self.masterBarButtonItem animated:YES];
}
/* forward the message to the current detail view
* all detail views must implement UISplitViewControllerDelegate
*/
-(void)splitViewController:(UISplitViewController *)svc willShowViewController:(UIViewController *)aViewController invalidatingBarButtonItem:(UIBarButtonItem *)barButtonItem
{
//if (![self.currentDetailController isKindOfClass:[SyncSourceDetailViewController class]])
//{
self.masterBarButtonItem = nil;
self.masterPopoverController = nil;
[self.currentDetailController.navigationItem setLeftBarButtonItem:nil animated:YES];
//}
}
Similar methods exist in the detail view controllers themselves.
The method that I'm calling when the notification is received is as follows:
- (void)navigateToLatest
{
[self navigateToDocumentsTab];
[[self dataLoader] navigateToLatest]; // populates data for the left table view
UISplitViewController *splitViewController = (UISplitViewController *)self.window.rootViewController;
UIView *view = [[[[[splitViewController viewControllers] objectAtIndex:1] viewControllers] objectAtIndex:0] view];
[self.masterDetailManager.masterPopoverController presentPopoverFromRect:CGRectMake(0, 0, 100, 100) inView:view permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
}
This does not work. I've also tried using the presentPopoverFromBarButtonItem method but I cannot seem to locate the leftBarButtonItem in code.
Maybe I am going about this completely the wrong way.
I ended up solving this problem by setting a "showSidebar" variable in my app delegate to true in the navigateToLatest method. Then in my DetailViewController's ViewDidAppear event I check the value of this variable, if it is true then I show the sidebar using the following code:
UIPopoverController *masterPopoverController= [[theAppDelegate masterDetailManager] masterPopoverController];
[masterPopoverController presentPopoverFromBarButtonItem:[self.navigationItem leftBarButtonItem] permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
[theAppDelegate setShowSidebar:NO];
My app was working perfectly prior to iOS 5.1. It is a UISplitviewController with a UINavigationController in the left-hand pane, and updates the detail pane dependent upon choices made within that UINavigationController.
The new sliding-in from left replacement for the popover, endemic to iOS 5.1 works; it doesn't look great, but it works.
What appears to be broken is the display of the 'popover' from the bar button in portrait. It causes an exception - 'NSInternalInconsistencyException', reason: 'Unknown direction passed to _popoverViewSizeForContentSize:arrowDirection:'
I have tried to override the method which displays the popover as follows:
-(void)splitViewController:(UISplitViewController *)svc popoverController:(UIPopoverController *)pc willPresentViewController:(UIViewController *)aViewController
{
[self.popoverController presentPopoverFromBarButtonItem:self.masterPopoverButtonItem permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
}
But I still get the same error. Can anyone help?
Use the below delegates to display the master page from details page
- (void)splitViewController:(UISplitViewController *)splitController willHideViewController:
(UIViewController *)viewController withBarButtonItem:(UIBarButtonItem *)barButtonItem forPopoverController:(UIPopoverController *)popoverController
{
barButtonItem.title = NSLocalizedString(#"Master", #"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;
}