I'm following a demo about hiding the left side of a Split View when the iPad is in Portrait, and then showing it as a Popover when a button placed in a toolbar is pressed. I followed everything step-by-step and it works (sort of) except that when I press the button, instead of in a Popover the view is displayed sliding from left to right. I also noticed that I can get the same result by sliding my finger from left to right instead of pressing the button.
This is the code I use (same code works properly in the demo).
in the View Controller of the view that should be shown in the Popover:
-(BOOL)splitViewController:(UISplitViewController*)svc
shouldHideViewController:(UIViewController*)vc
inOrientation:(UIInterfaceOrientation)orientation
{
return [self splitViewBarButtonItemPresenter] ? UIInterfaceOrientationIsPortrait(orientation) : NO;
}
-(void)splitViewController:(UISplitViewController *)svc
willHideViewController:(UIViewController *)aViewController
withBarButtonItem:(UIBarButtonItem *)barButtonItem
forPopoverController:(UIPopoverController *)pc
{
barButtonItem.title = self.title;
[self splitViewBarButtonItemPresenter].splitViewBarButtonItem = barButtonItem;
}
-(void)splitViewController:(UISplitViewController *)svc
willShowViewController:(UIViewController *)aViewController
invalidatingBarButtonItem:(UIBarButtonItem *)barButtonItem
{
[self splitViewBarButtonItemPresenter].splitViewBarButtonItem = nil;
}
And in the View that should display the Popover:
-(void)setSplitViewBarButtonItem:(UIBarButtonItem *)splitViewBarButtonItem
{
if (_splitViewBarButtonItem != splitViewBarButtonItem){
NSMutableArray *toolbarItems = [self.toolbar.items mutableCopy];
if (_splitViewBarButtonItem) [toolbarItems removeObject:_splitViewBarButtonItem];
if (splitViewBarButtonItem) [toolbarItems insertObject:splitViewBarButtonItem atIndex:0];
self.toolbar.items = toolbarItems;
_splitViewBarButtonItem = splitViewBarButtonItem;
}
}
SplitViewBarButtonItemPresenter is just a protocol used to delegate who should show (you guessed it!) the button for the popover.
Related
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.
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.
In iOS 7 UIPopoverControllers have the parallax effect (foreground hovering over background).
This is especially ugly in a UISplitViewController that is in portrait mode.
Lines are not on the same level. No matter how you hold it, initially the popover is 4-5 pixels above where it should be
There are no separators (not even hairlines) on top / at the bottom of the popover. This results in the popover looking even more missplaced.
Has anyone found a good workaround/fix for this?
Edit:
Edit 2:
UIActionSheet also has this parallax effect.
Edit 3:
My delegate method that is somehow related to the presentation of the master
- (void)splitViewController:(UISplitViewController *)splitController willHideViewController:(UIViewController *)viewController withBarButtonItem:(UIBarButtonItem *)barButtonItem forPopoverController:(UIPopoverController *)popoverController
{
barButtonItem.title = NSLocalizedString(#"Übersicht", nil);
[self.navigationItem setLeftBarButtonItem:barButtonItem animated:YES];
self.masterPopoverController = popoverController;
}
A little bit hacky, but works for UISplitViewController:
- (void)splitViewController:(UISplitViewController *)svc popoverController:(UIPopoverController *)pc willPresentViewController:(UIViewController *)aViewController
{
UIView *popoverView = [[aViewController.view superview] superview];
popoverView.motionEffects = #[];
}
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];
I am using the splitViewController template provided by Apple. On a specific action, I want to show the rootViewController. Unfortunately, I cannot find a method that will show the popover (programmatically) just as it does when you tap the bar button item.
Any Ideas? Thanks!
you can show the popover from a barButtonItem or with your own rect with these two methods:
[self.popoverController presentPopoverFromRect:(CGRect) inView:(UIView *) permittedArrowDirections:(UIPopoverArrowDirection) animated:(BOOL)];
[self.popoverController presentPopoverFromBarButtonItem:(UIBarButtonItem *) permittedArrowDirections:(UIPopoverArrowDirection) animated:(BOOL)]
self.popoverController is my case an ivar which stores the popover. I'm setting this variable each time in:
- splitViewController:willHideViewController:withBarButtonItem:forPopoverController:
and set it back to nil in it's counterpart method:
- splitViewController:willShowViewController:invalidatingBarButtonItem:
heres my code:
- (void)splitViewController: (UISplitViewController*)svc willHideViewController:(UIViewController *)aViewController withBarButtonItem:(UIBarButtonItem*)barButtonItem forPopoverController: (UIPopoverController*)pc {
barButtonItem.title = #"42";
self.navigationController.navigationBar.topItem.leftBarButtonItem = barButtonItem;
self.popoverController = pc;
}
- (void)splitViewController: (UISplitViewController*)svc willShowViewController:(UIViewController *)aViewController invalidatingBarButtonItem:(UIBarButtonItem *)barButtonItem {
self.navigationController.navigationBar.topItem.leftBarButtonItem = nil;
self.popoverController = nil;
}