Parrots
Parrots

Reputation: 26902

Hiding UISplitViewController overlay in portrait

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)

Upvotes: 7

Views: 6101

Answers (6)

malhal
malhal

Reputation: 30754

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

Upvotes: 0

JBJr
JBJr

Reputation: 109

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;
        }];
    }
}

Upvotes: 2

Alexecc
Alexecc

Reputation: 1

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

Upvotes: 0

Gank
Gank

Reputation: 4667

@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;
}

Upvotes: -1

Cory Juhlin
Cory Juhlin

Reputation: 384

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.

Upvotes: 6

LaborEtArs
LaborEtArs

Reputation: 2043

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

Upvotes: 3

Related Questions