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)
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.
Just place your code (without the return NO;
) in the master view controllers prepareForSegue:sender:
or tableView:didSelectRowAtIndexPath:
method.
There it works perfectly!
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;
}];
}
}
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
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
@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;
}