Hiding UISplitViewController overlay in portrait

2019-04-26 00:41发布

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)

6条回答
Melony?
2楼-- · 2019-04-26 01:22

Easily achieved using the split-view delegate as follows:

@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
查看更多
虎瘦雄心在
3楼-- · 2019-04-26 01:23

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;
        }];
    }
}
查看更多
趁早两清
4楼-- · 2019-04-26 01:23

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

查看更多
Anthone
5楼-- · 2019-04-26 01:28

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

查看更多
forever°为你锁心
6楼-- · 2019-04-26 01:36

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.

查看更多
The star\"
7楼-- · 2019-04-26 01:41
@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;
}
查看更多
登录 后发表回答