21

I have created a new split view based project in my XCode 4.2

Then in DetailViewController.m file i have add this method

- (BOOL)splitViewController: (UISplitViewController*)svc shouldHideViewController:(UIViewController *)vc inOrientation:(UIInterfaceOrientation)orientation 
{
  //This method is only available in iOS5  

   return NO;
}

Now by doing this i can able to show both left & right part of my splitview Controller at a time.

Now i have added a UIBarButtonItem in my DetailViewController Navigation bar and i want by using which i can hide & show my Master View both in Portrairt and Landscape Mode.

- (IBAction)hideUnhide:(id)sender 
{

//How can hide & unhide

}

How can i do this?

Dalee Davis
  • 981
  • 8
  • 19
user930195
  • 432
  • 1
  • 5
  • 19

11 Answers11

8
instead spv.delegate=nil; spv.delegate=self;

you need to do next:

[spv willRotateToInterfaceOrientation:self.interfaceOrientation duration:0];
Philip J. Fry
  • 1,165
  • 1
  • 8
  • 10
  • Ha! I've been looking everywhere for a solution to this. Nice job! PS - Love Futurama. – DenVog Sep 26 '12 at 22:23
  • spv.delegate=nil; spv.delegate=self -> Working -- [spv willRotateToInterfaceOrientation:self.interfaceOrientation duration:0] -> Not Working – kinghomer Oct 19 '12 at 12:24
  • works great for iOS 6.0, while I've also have to set `contentMode = UIViewContentModeRedraw` for some custom views in the details panel, cause otherwise they haven't resized properly. – Dmitry Sokurenko Nov 05 '12 at 16:42
  • 2
    while still don't forget to call `splitViewController.view.setNeedsLayout`, just calling `willRotateToInterfaceOrientation` seems to be not enought – Dmitry Sokurenko Nov 05 '12 at 16:45
  • In iOS 7, you don't need to set the delegate to `nil` and then to `self`, just call `[self.splitViewController willRotateToInterfaceOrientation:self.interfaceOrientation duration:0];` then on the next line `[self.splitViewController.view setNeedsLayout];`, to trigger show/hide the master view controller. – paulvs Feb 28 '14 at 13:59
7

'setNeedsLayout' makes UISplitViewController to ask for "shouldHideViewController"

- (IBAction)hideUnhide:(id)sender  {
    UISplitViewController* spv = ...;

    self.hideMaster= !self.hideMaster;
    [ spv.view setNeedsLayout ]
}
Andrei Tchijov
  • 559
  • 5
  • 11
  • I have been looking all over to find a way to get the split view to ask this. Don't know why I didn't think of it. Thanks! – sasquatch Mar 28 '12 at 20:58
  • 7
    It seems that the way SplitViewController (SVC) uses shouldHideViewController on delegate has changed in 5.1. Now SVC would invoike shouldHideViewController with all possible orientations ONCE when you set delegate (and it will not do it ever again). So if you want to "inform" that shouldHideViewController "change its mind", its not enough to use setNeedsLayout on SVC.view. You have to change delegate value on SVC. NOTE: its not enough to 're-assign' delegate to self. It seems that SVC checking if delegate is actually different from currently assigned. – Andrei Tchijov Mar 29 '12 at 21:15
  • I was changing the delegate, but that wasn't enough the get the view to hide or show the master. In my app I have one view that always hides the master, but when popping to this view the master still showed, even though it was the delegate. Once I rotated, it worked as planned. But the setNeedsLayout in the viewWillAppear has fixed that. – sasquatch Apr 02 '12 at 20:28
  • Hi Andrei, thanks for the reply. But recently i have created a sample splitview based project (XCode 4.3.2 & iOS 5.1)and do as it is mentioned here but only showing of left side root view controller all the time in Both orientation is happening correctly.But when i create a button in my detail view controller and call the hideUnhide: nothing is happening.The hiding and unhiding of left side root view controller is not happening. – raaz Jun 07 '12 at 09:40
6

In iOS 5.1 you have to do it this way:

Inside DetailViewController.m

- (IBAction)hideUnhide:(id)sender  {
    UISplitViewController* spv = ...;

    self.hideMaster= !self.hideMaster;

    [spv.view setNeedsLayout];
    spv.delegate = nil;
    spv.delegate = self;
}

- (BOOL)splitViewController:(UISplitViewController*)svc shouldHideViewController:(UIViewController *)vc inOrientation:(UIInterfaceOrientation)orientation {
    return self.hideMaster;
}
Manni
  • 11,108
  • 15
  • 49
  • 67
  • 1
    Hi Manni, thanks for the reply. But recently i have created a sample splitview based project (XCode 4.3.2 & iOS 5.1)and do as it is mentioned here but only showing of left side root view controller all the time in Both orientation is happening correctly.But when i create a button in my detail view controller and call the hideUnhide: nothing is happening.The hiding and unhiding of left side root view controller is not happening. – raaz Jun 07 '12 at 09:40
4

I combined the above responses and the following works well in IOS 6:

// In split delegate
-(void)hideMaster:(BOOL)hideState
{
   _masterIsHidden = hideState;

   [self.splitViewController.view setNeedsLayout];
   self.splitViewController.delegate = nil;
   self.splitViewController.delegate = self;

   [self.splitViewController willRotateToInterfaceOrientation:[UIApplication    sharedApplication].statusBarOrientation duration:0];
}

-(BOOL)splitViewController:(UISplitViewController *)svc shouldHideViewController:(UIViewController *)vc inOrientation:(UIInterfaceOrientation)orientation
{
    return self.masterIsHidden;
}
Rudy
  • 939
  • 9
  • 6
3
-(IBAction)clickToShowMaster:(id)sender
{
 UIBarButtonItem *systemItem1 = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"prev.png"] style:UIBarButtonItemStylePlain target:self action:@selector(clickToHidemaster:)];
 self.navigationItem.leftBarButtonItem = systemItem1;
[self.tabBarController.tabBar setHidden:NO];
[self hideMaster:NO];
}
-(void)hideMaster:(BOOL)hideState
{

ishideMaster=hideState;
[self.splitViewController.view setNeedsLayout];
self.splitViewController.delegate = nil;
self.splitViewController.delegate = self;

[self.splitViewController willRotateToInterfaceOrientation:[UIApplication    sharedApplication].statusBarOrientation duration:0];

 }

-(void)hideMaster:(BOOL)hideState
{
ishideMaster=hideState;
[self.splitViewController.view setNeedsLayout];
self.splitViewController.delegate = nil;
self.splitViewController.delegate = self;

[self.splitViewController willRotateToInterfaceOrientation:[UIApplication    sharedApplication].statusBarOrientation duration:0];

}


    #pragma mark - Split view

-(BOOL)splitViewController:(UISplitViewController *)svc shouldHideViewController:(UIViewController *)vc inOrientation:(UIInterfaceOrientation)orientation
{

    if(UIInterfaceOrientationIsPortrait(orientation))
    {
        UIBarButtonItem *systemItem1 = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"down.png"] style:UIBarButtonItemStylePlain target:self action:@selector(showPopup)];
        self.navigationItem.leftBarButtonItem = systemItem1;
        [self setUIforPortrait];
        return YES;
    }
     if (UIInterfaceOrientationIsLandscape(orientation))
    {
        if(ishideMaster==TRUE)
        {
            UIBarButtonItem *systemItem1 = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"next.png"] style:UIBarButtonItemStylePlain target:self action:@selector(clickToShowMaster:)];
            self.navigationItem.leftBarButtonItem = systemItem1;
            [self setUIForFullLandscape];
        }
        else
        {
            UIBarButtonItem *systemItem1 = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"prev.png"] style:UIBarButtonItemStylePlain target:self action:@selector(clickToHidemaster:)];
            self.navigationItem.leftBarButtonItem = systemItem1;
            [self setUIForHalfLandscape];
        }
        return ishideMaster;
    }

}
//add the navigation button on left top, to pop-up master view.
- (void)splitViewController:(UISplitViewController *)splitController willHideViewController:(UIViewController *)viewController withBarButtonItem:(UIBarButtonItem *)barButtonItem forPopoverController:(UIPopoverController *)popoverController
{
    [barButtonItem setImage:[UIImage imageNamed:@"down.png"]];

    UIBarButtonItem *systemItem1 = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"down.png"] style:UIBarButtonItemStylePlain target:self action:@selector(showPopup)];
    self.navigationItem.leftBarButtonItem = systemItem1;
   self.masterPopoverController = popoverController;
    self.masterPopoverController.delegate=self;
}

- (void)splitViewController:(UISplitViewController *)splitController willShowViewController:(UIViewController *)viewController invalidatingBarButtonItem:(UIBarButtonItem *)barButtonItem
{
    // Called when the view is shown again in the split view, invalidating the button and popover controller.
    //;
    if(UIInterfaceOrientationIsLandscape(self.interfaceOrientation))
    {
        if(ishideMaster==FALSE)
        {
            UIBarButtonItem *systemItem1 = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"prev.png"] style:UIBarButtonItemStylePlain target:self action:@selector(clickToHidemaster:)];
            self.navigationItem.leftBarButtonItem = systemItem1;
        }
        else
        {
            UIBarButtonItem *systemItem1 = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"next.png"] style:UIBarButtonItemStylePlain target:self action:@selector(clickToShowMaster:)];
            self.navigationItem.leftBarButtonItem = systemItem1;

        }
    }
    else if(UIInterfaceOrientationIsPortrait(self.interfaceOrientation))
    {
        [self.navigationItem setLeftBarButtonItem:nil animated:YES];

    }
   //self.masterPopoverController = nil;
}
2

In iOS8 this is easy.

To hide it

[UIView animateWithDuration:0.2 animations:^{
    splitViewController.preferredDisplayMode = UISplitViewControllerDisplayModePrimaryHidden;
} completion:nil];

To show it

[UIView animateWithDuration:0.2 animations:^{
    self.splitViewController.preferredDisplayMode = UISplitViewControllerDisplayModePrimaryOverlay;
} completion:nil];
trapper
  • 11,716
  • 7
  • 38
  • 82
1

Regarding wzbozon's comment about not needing to reassign the delegate, I found that the two lines

self.splitViewController.delegate = nil;

self.splitViewController.delegate = self;

...were not needed on the simulator but were needed on my iOS5 iPad 1. Without them, the hide/show behavior did not occur (clicking the button did not collapse the master view).

Cat
  • 66,919
  • 24
  • 133
  • 141
Jack Bellis
  • 1,177
  • 8
  • 10
0

You can show/hide the master ViewController by triggering the action of the displayModeButtonItem property of UISplitViewController:

Swift

if let displayModeButtonItem = splitViewController?.displayModeButtonItem() {
    displayModeButtonItem.target?.performSelector(displayModeButtonItem.action)
}

Objective-C

UIBarButtonItem *displayModeButtonItem = [self.splitViewController displayModeButtonItem];
[displayModeButtonItem.target performSelector: displayModeButtonItem.action];

It feels more proper to me than meddling with the delegate, the orientation and the layout at the same time.

Raphaël
  • 3,646
  • 27
  • 28
0

SWIFT 3.0

I've used

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier == "showDetail" {
            if let indexPath = self.tableView.indexPathForSelectedRow {
                let object = self.exercises[indexPath.row] 
                let controller = (segue.destination as! UINavigationController).topViewController as! DetailViewController
                controller.detailItem = object
                controller.navigationItem.leftBarButtonItem = self.splitViewController?.displayModeButtonItem
                controller.navigationItem.leftItemsSupplementBackButton = true
                UIView.animate(withDuration: 0.2, delay: 0.0, options: [.curveEaseOut], animations: {
                    self.splitViewController?.preferredDisplayMode = .primaryHidden
                }, completion: nil)

            }
        }
    }
catu
  • 888
  • 6
  • 24
0

Well, the easy part of your question is to use a bool, say a property hideMaster, and then

- (IBAction)hideUnhide:(id)sender 
{

   self.hideMaster= !self.hideMaster;

}

and then...

- (BOOL)splitViewController: (UISplitViewController*)svc shouldHideViewController:(UIViewController *)vc inOrientation:(UIInterfaceOrientation)orientation 
{
  //This method is only available in iOS5  

   return self.hideMaster;
}

That works fine, but the shouldHideViewController is only called during a redraw of the splitVC, such as during a rotation, so the master only hides/unhides then.

mackworth
  • 5,873
  • 2
  • 29
  • 49
  • I created a test project (using Xcode's "master Detail" template"), then added those two routines to DetailVC.m, added the property hideMaster, and added a UIBarButton to the detail bar, and linked to hideUnhide. Runs in simulator with Master appearing/disappearing with button. Does NOT hide/unhide the master immediately, but upon the next rotation, so it's not a complete solution. – mackworth Nov 11 '11 at 20:18
-1
- (BOOL)splitViewController:(UISplitViewController*)svc shouldHideViewController:(UIViewController *)vc inOrientation:(UIInterfaceOrientation)orientation {
    [spv.view setNeedsLayout];  //add the line
    return self.hideMaster;
}
  • The app won't start if i do so. Some people set the splitViewControllerDelegate right after app launch, so I won't recomend to do so. – Vivienne Fosh May 23 '13 at 18:14