7

In adopting the new UISplitViewController I'm trying to make a change a default behaviour that occurs when using the UISplitViewControllerDisplayModeAutomatic mode.

When working in portrait I want the primary overlay to hide when the user triggers a push to the detail side. By default the overlay remains onscreen until the user taps over on the detail side.

I've tried using the following with the delegate:

- (BOOL)splitViewController:(UISplitViewController *)splitViewController showDetailViewController:(UIViewController *)vc sender:(id)sender
{
    if (splitViewController.displayMode == UISplitViewControllerDisplayModePrimaryOverlay) {
        [UIView animateWithDuration:0.3 animations:^{
            splitViewController.preferredDisplayMode = UISplitViewControllerDisplayModePrimaryHidden;
        }];
    }
    return NO;
}

This gives me the desired behavior in portrait, but this breaks landscape mode (which I want to behave like UISplitViewControllerDisplayModeAllVisible). If you've done a push and then rotate the device the left side is still hidden (as expected). I can't find an appriprite place to hook in to re-set the prefered mode on rotation to show the left side (since trait collections can't be used to tell landscape vs portrait on the iPad).

How can I manually trigger a dismissal of the overlay?

Dupe note: iOS8 has changed UISplitViewController entirely, so all other SO answers before June '14 are probably wrong (and I've dug through many of them, just incase)

Dharmesh Kheni
  • 71,228
  • 33
  • 160
  • 165
Parrots
  • 26,658
  • 14
  • 59
  • 78

6 Answers6

6

I was having the same problem as you. I am doing this on Xamarin's mono touch platform, but I would think the result would be the same.

Like what LaborEtArs said, move your code to the prepareForSegue:sender: method of the master view controller. Then just set the mode to automatic after you set it to hidden:

if (splitViewController.displayMode == UISplitViewControllerDisplayModePrimaryOverlay) {
    splitViewController.preferredDisplayMode = UISplitViewControllerDisplayModePrimaryHidden;
    splitViewController.preferredDisplayMode = UISplitViewControllerDisplayModeAutomatic;
}

After doing it this way, it's no longer breaking landscape mode.

Cory Juhlin
  • 364
  • 4
  • 9
3

Just place your code (without the return NO;) in the master view controllers prepareForSegue:sender: or tableView:didSelectRowAtIndexPath: method. There it works perfectly!

LaborEtArs
  • 1,938
  • 23
  • 27
  • Nice simple answer. I found the duration of 0.3 to be a little slow compared to the animation when you toggle in or out with a swipe or button; 0.2 seconds seems about right. – Nick Nov 09 '14 at 09:12
  • This work great but if the master view is showing when the device is rotated to landscape the detail view doesn't resize appropriately. – Leon Jul 02 '15 at 19:05
2

In addition to the advice from LaborEtArs to do your animation in prepareForSegue:sender: or tableView:didSelectRowAtIndexPath:, if your app normally has splitViewController:preferredDisplayMode set to UISplitViewControllerDisplayModeAutomatic, simply use the animateWithDuration: method with a completion handler to restore the displayMode after the animation:

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    if ([[segue identifier] isEqualToString:@"showDetail"]) {

        // configure the detail controller

        if (self.splitViewController.displayMode == UISplitViewControllerDisplayModePrimaryOverlay) {
            [UIView animateWithDuration:0.3 animations:^{
                self.splitViewController.preferredDisplayMode = UISplitViewControllerDisplayModePrimaryHidden;
            } completion:^(BOOL finished){
                self.splitViewController.preferredDisplayMode = UISplitViewControllerDisplayModeAutomatic;
        }];
    }
}
malhal
  • 26,330
  • 7
  • 115
  • 133
JBJr
  • 109
  • 2
  • 7
  • The animation is really important for making this happen. Kudos. However, placing this in the `didSelectRowAtIndexPath` causes a weird detail tableview animation (undesirable). Placing it in `prepareForSegue:sender` results in a brilliant animation. – David Apr 17 '16 at 22:45
0

Here is the Swift version:

if (self.splitViewController?.displayMode == UISplitViewControllerDisplayMode.PrimaryOverlay){
    splitViewController?.preferredDisplayMode = UISplitViewControllerDisplayMode.PrimaryHidden
    splitViewController?.preferredDisplayMode = UISplitViewControllerDisplayMode.Automatic
} else {
    println(self.splitViewController?.displayMode)
}

Placed in the prepareForSegue

Steve Madsen
  • 13,465
  • 4
  • 49
  • 67
Alexecc
  • 1
  • 2
0

iOS 13 and later

This method isn't in the public header but you can add it as follows:

@interface UISplitViewController()
- (void)toggleMasterVisible:(id)sender;
@end

Before iOS 13

I used to use this way but no longer no longer works in iOS 13's new UISplitViewControllerPanelImpl:

@interface AppDelegate ()
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
@property (nonatomic, assign) UIPopoverController *splitPopoverController;
#pragma clang diagnostic pop
@end

@implementation AppDelegate

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
- (void)splitViewController:(UISplitViewController *)splitViewController popoverController:(UIPopoverController *)popoverController willPresentViewController:(UIViewController *)vc{
#pragma clang diagnostic pop
    self.splitPopoverController = popoverController;
}

- (BOOL)splitViewController:(UISplitViewController *)splitViewController showDetailViewController:(UIViewController *)vc sender:(nullable id)sender{
    [self.splitPopoverController dismissPopoverAnimated:YES];
    return NO;
}

@end
malhal
  • 26,330
  • 7
  • 115
  • 133
-1
@implementation SplitProductView

- (void)viewDidLoad {
    [super viewDidLoad];
    self.delegate = self;

}


- (void)viewWillAppear:(BOOL)animated{
    [self resetSplit:[[UIApplication sharedApplication] statusBarOrientation]];

    [super viewWillAppear:animated];
}

-(void)resetSplit :(UIInterfaceOrientation)toInterfaceOrientation {

    //TODOX:iphone
    if (isPad)
    {
        if(UIInterfaceOrientationIsPortrait(toInterfaceOrientation)){
            self.preferredDisplayMode = UISplitViewControllerDisplayModePrimaryOverlay;
        }
        else{
            //if (self.displayMode == UISplitViewControllerDisplayModePrimaryOverlay)
            {
                self.preferredDisplayMode = UISplitViewControllerDisplayModePrimaryHidden;
                self.preferredDisplayMode = UISplitViewControllerDisplayModeAllVisible;
                self.preferredDisplayMode = UISplitViewControllerDisplayModeAutomatic;

            }
        }
    }
}

- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
    if (isPad)
    {
        if (!UIInterfaceOrientationIsPortrait([[UIApplication sharedApplication] statusBarOrientation])){
            self.preferredDisplayMode =UISplitViewControllerDisplayModePrimaryOverlay;
        }
    }

    [self resetSplit:toInterfaceOrientation];
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}


- (BOOL)splitViewController:(UISplitViewController *)splitViewController
collapseSecondaryViewController:(UIViewController *)secondaryViewController
  ontoPrimaryViewController:(UIViewController *)primaryViewController {
    return YES;
}
Gank
  • 4,507
  • 4
  • 49
  • 45