0

I am updating an old app to the new adaptive size way of doing things and having difficulty getting a popover with a navigation controller to work.

My goal: I want to be able to open a popover from a button when the app is either compact and regular horizontal. The popover has a tableview and uses a navigation controller to push view controllers when the user touches a row on the table. I can get the popover to open correctly, but I can’t figure out who to make the pushes work.

Here’s the code that opens the popover:

OptionsController *vc = [[OptionsController alloc] initWithNibName:@"OptionsView" bundle:nil];
vc.modalPresentationStyle = UIModalPresentationPopover;
UIPopoverPresentationController *popover = [vc popoverPresentationController];
popover.delegate = self;
[self presentViewController:vc animated: YES completion: nil];

popover.permittedArrowDirections = UIPopoverArrowDirectionUp;  // change as necessary
popover.sourceView = self.view;
CGRect popoverRect = [self.view convertRect:[sender frame] fromView:[sender superview]];
popover.sourceRect = popoverRect;

This code correctly opens a popover in either compact or regular size.

In the OptionsController’s didSelectRowAtIndexPath method, I have this(controllersArray is an array of UIViewControllers, each of which corresponds to a row in the table):

    UIViewController *nextController = [self.controllersArray objectAtIndex: [indexPath row]];
    [self.navigationController pushViewController:nextController animated:YES];

All this executes, but no push occurs, so the next view never appears.

I clearly am not understanding something about using the UIViewController’s navigationController, or how to install a navigationController to make this work. After three or four days of digging around to try to understand how to make this work, I'd appreciate any insights, or links to documentation about how to do this. Thanks in advance.

johnz
  • 489
  • 2
  • 17
  • Where you want to push vc.. in your main vc from which popover is opening or in pop over vc itself. – Ashish P. Aug 14 '15 at 08:44
  • I want to push view controllers from the popover. – johnz Aug 14 '15 at 09:03
  • plz look at this link http://stackoverflow.com/questions/5166291/navigation-controller-in-popover – Ashish P. Aug 14 '15 at 09:07
  • The link cited was done before popovers were implemented as presentationControllers. If I push the navigation controller, I get a modal fullscreen view - not what I want. I want to be able to open a popover - as a presentation controller - that has a working navigation controller in it. I haven't figure out how to do that in iOS8/9. – johnz Aug 14 '15 at 10:00

2 Answers2

2

Crud - this has a very easy answer. Just took me thinking a different way and digging through Larcerax's comments. Here's how to make this work:

    OptionsController *vc = [[OptionsController alloc] initWithStyle:UITableViewStylePlain];
    vc.title = @"Options";
    UINavigationController *nc = [[UINavigationController alloc] initWithRootViewController: vc];
    nc.modalPresentationStyle = UIModalPresentationPopover;
    UIPopoverPresentationController *popover = [nc popoverPresentationController];
    popover.delegate = self;
    [self presentViewController:nc animated: YES completion: nil];

    popover.permittedArrowDirections = UIPopoverArrowDirectionUp;  // change as necessary
    popover.sourceView = self.view;
    CGRect popoverRect = [self.view convertRect:[sender frame] fromView:[sender superview]];
    popover.sourceRect = popoverRect;

The difference is that I create the UINavigationController in the usual manner...

        UINavigationController *nc = [[UINavigationController alloc] initWithRootViewController: vc]; 

...set the navigationController's presentation style to popover, then get the popoverPresentationController from the navigationController - before I was doing those two methods on the UIViewController.

    nc.modalPresentationStyle = UIModalPresentationPopover;
    UIPopoverPresentationController *popover = [nc popoverPresentationController];

Finally, I present the navigationController:

    nc.modalPresentationStyle = UIModalPresentationPopover;
    UIPopoverPresentationController *popover = [nc popoverPresentationController];

This approach presents a popover, in compact and regular horizontal sizes, that contains navigation controller functionality: just what I wanted.

Thanks again to Larcerax whose answer wasn't what I needed, but made me re-think what I was doing in a different way. As usual, StackOverflow comes through.

johnz
  • 489
  • 2
  • 17
0

Here's what you should try. I use about 10-40 navigation controllers per app for the apps I work on and I've had this same sort of issue, I've not used popovers, but I've subclassed the crap out of navigation controllers and view controllers to have encountered your same problem. The thing is that if you do this:

 UIViewController * ff = [UIViewController new]
 [self presnetViewController:ff  ... blah blah blah

There is apparently NO navigation system attached to the modal view controller and therefore you can't navigate to anything else, you can only close the modal and move on. So, this is what I do to resolve this and it works everytime, well it works everytime for UIViewControllers, give it a shot

see the following, it's not for popovers, but the principle is the same:

NSHTermsOfServiceViewController * pvc = [NSHTermsOfServiceViewController new];
UIBarButtonItem * backBarButtonItem = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"exit-button"] style:UIBarButtonItemStylePlain target:self action:@selector(backerPressed)];
NSHNavigationController * ssf = [[NSHNavigationController alloc] initWithRootViewController:pvc];
[[pvc navigationItem] setLeftBarButtonItem:backBarButtonItem];
[[self navigationController] presentViewController:ssf animated:true completion:nil];

NSHTermsOfServiceViewController <== is a subclass of another subclass of a UIViewcontroller, it's basically a UIViewController on steroids, that's all

NSHNavigationController is a UINavigationController that is subclassed and pumped up on steroids for animations

The flow is this:

  1. create a viewController
  2. create a new UINavigationController
  3. Set the view controller you created in step 1 as the root view controller of the navigation controller created in step 2
  4. present the NAVIGATION CONTROLLER, not the UIViewController, you can present this navigationController from a view controller like so ..

v

[self presentViewController:ssf animated:true completion:nil];

or you can present it from the current view controller's navigation controller which is what I prefer, like so:

[[self navigationController] presentViewController:ssf animated:true completion:nil];

Your code, modified, the only problem is that I don't know if you can present a UIPopOverViewController by rooting it inside a navigation controller

OptionsController *vc = [[OptionsController alloc] initWithNibName:@"OptionsView" bundle:nil];

UIPopoverPresentationController *popover = [vc popoverPresentationController];


UINavigationController * stuff = [[NSHNavigationController alloc] initWithRootViewController:popover];
stuff.modalPresentationStyle = UIModalPresentationPopover;
stuff.delegate = self;

[self.navigationController presentViewController:stuff animated: YES completion: nil];

popover.permittedArrowDirections = UIPopoverArrowDirectionUp;  // change as necessary
popover.sourceView = self.view;
CGRect popoverRect = [self.view convertRect:[sender frame] fromView:[sender superview]];
popover.sourceRect = popoverRect;

Yep, my bad, doesn't work for popover, I just tried it

So, with that said, is it absolutely necessary to use a popover? Why are you using this and now just a UIViewcontroller that you reconfigure to look like a popover and then you have what you need?

Here's this, I just tried it with the Ipad simulator, and it allowed a push, just as it should have.

NSHLoginViewController * pvc = [NSHLoginViewController new];

UINavigationController *navController = [[UINavigationController alloc]initWithRootViewController:pvc];
UIPopoverController *popover = [[UIPopoverController alloc] initWithContentViewController:navController];

UIView * stuff = [[UIView alloc] initWithFrame:CGRectMake(50, 50, 1000, 1000)];
[self.view addSubview:stuff];
[popover presentPopoverFromRect:[[self contentView] nameField].frame inView:stuff permittedArrowDirections:UIPopoverArrowDirectionUp animated:YES];

this: [[self contentView] nameField].frame is just a uitextfield, nothing special, that's all, and the method above presented the login viewcontroller, when I put in my credentials, I pressed log in and it pusehed the next viewcontroller as it normally would, there's probalby something wrong with the touches being intercepted by your uitableview or whatever, perhaps not, but this method did work for me.

Larry Pickles
  • 4,615
  • 2
  • 19
  • 36
  • Many thanks for this detailed explanation. It seems to me that if I present the navigation controller, I get a modal view, not a popover, which is what I really want. I will give your suggestions a try and see if I can apply the principles to popovers. Thanks again for your help. – johnz Aug 14 '15 at 09:03
  • read the rest of my answer above, not sure if it will help you, but I did indeed get it to push another view controller onto the stack from the popover, just do it the reverse of the way I was showing you, put the navigation controller as the contents of the popover, it all makes sense, then this will give the viewcontroller in the pop over the ability to navigate – Larry Pickles Aug 14 '15 at 09:10
  • One problem I see with this solution is that it uses presentPoverFromRect: which is the old, deprecated way of presenting a popover. I'd really like to use the new way, with the popover as a presentationController, presented using presentViewController method of presentationController. Still, if it works, I may go with it. – johnz Aug 14 '15 at 10:11
  • Yeah, that's a good point, I'm going to try the other way, let me see what it can do – Larry Pickles Aug 14 '15 at 10:12