Azu
Azu

Reputation: 322

iOS UISplitViewController's Popover controller button disappear after pushing new view controller in portrait mode

In my UISplitViewController application, I have

When one item (which is in a UITableView) in RootViewController is tapped, new view controller will be set as the following shows:

[detailViewController setViewControllers:[NSArray arrayWithObjects:newViewController, nil] animated:animated];

//detailPane is my DetailViewController

All works pretty well in landscape mode. However, I can't make the UISplitViewController work as what I want in portrait mode, that is, the RootViewController's popover button does not appear appropriately in my DetailViewController when I launch and use the application in portait mode.

When I launch the app in portrait mode, the popover button appears appropriately. But after tapping one item in the popover and a new view controller has been set on detailViewController, the button disappeared. I have to rotate the device to landscape and then back to portrait again to make the button appear again.

I set my UISplitViewController's delegate in my application's AppDelegate as follows:

self.splitViewController.delegate = self.detailViewController

And here is my UISplitViewControllerDelegate implementation

- (void)splitViewController: (UISplitViewController*)svc willHideViewController:(UIViewController *)aViewController withBarButtonItem:(UIBarButtonItem*)barButtonItem  forPopoverController: (UIPopoverController*)pc {
    NSLog(@"Will hide view controller");
    barButtonItem.title = @"Menu";
    [self.navigationItem setLeftBarButtonItem:barButtonItem];
    self.popoverController = pc;
}

- (void)splitViewController: (UISplitViewController*)svc willShowViewController:(UIViewController *)aViewController invalidatingBarButtonItem:(UIBarButtonItem *)barButtonItem {
    NSLog(@"Will show view controller")
    NSMutableArray *items = [self.navigationItem.leftBarButtonItems mutableCopy];
    [items removeAllObjects];
    [self.navigationItem setLeftBarButtonItems:items animated:YES];
    [items release];
    self.popoverController = nil;   
}

Any hint or help is greatly appreciated. Thanks.

Upvotes: 7

Views: 10977

Answers (4)

Andrew Ash
Andrew Ash

Reputation: 41

I liked Nekto's solution, but it misses one key problem.

It's not clear what action: selector will cause the UISplitViewController to show the MasterViewController in a popover. When I finally figured this out, by examining the BarButtonItem in the debugger, I realized why it was so tricky to figure this out: the action: selector isn't documented anywhere in Apple's iOS SDK. Oops.

Try this:

UIBarButtonItem *showListView = [[UIBarButtonItem alloc] initWithTitle:@"List" style:UIBarButtonItemStyleBordered target:[self splitViewController] action:@selector(toggleMasterVisible:)];
[[detailViewController navigationItem] setLeftBarButtonItem:showListView];        

You may want to surround this code with a conditional that checks the window is in in portrait mode, such as if ([self interfaceOrientation] == UIInterfaceOrientationPortrait)

Upvotes: 2

Cato
Cato

Reputation: 81

Just came up with a new solution.

Subclass UINavigationController and implement UISplitViewControllerDelegate. Set an instance of this class as the right ViewController of the splitViewController. Everytime you want to change the detail view controller from the master

NewDetailViewController *newDetailVC = ....// Obtain the new detail VC

newDetailVC.navigationItem.leftBarButtonItem = [[[[self.splitViewController.viewControllers objectAtIndex:1]topViewController]navigationItem ]leftBarButtonItem];  //With this you tet a pointer to the button from the first detail VC but from the new detail VC

[[self.navigationController.splitViewController.viewControllers objectAtIndex:1]setViewControllers:[NSArray arrayWithObject:newDetailVC]];  //Now you set the new detail VC as the only VC in the array of VCs of the subclassed navigation controller which is the right VC of the split view Controller

This works for me and I can avoid defining a hole protocol and setting the master as the delegate, which is a big trade off. Hope it helps.

Upvotes: 4

Joris Weimar
Joris Weimar

Reputation: 4941

If you still need it:

http://developer.apple.com/library/ios/#samplecode/MultipleDetailViews/Introduction/Intro.html

What I did to my source (I had similar setup to you) to fix it:

I have the master viewcontroller (UITableViewController in my case) be the delegate of the UISplitViewController. In the two delegate methods for UISplitViewControllers (so this would be in your master viewcontroller implementation) you would save the popupviewcontroller and the barbuttonitem in your class. Now, if you change your details viewcontroller, you do:

self.viewControllers = [NSArray arrayWithObjects:[self.viewControllers objectAtIndex:0], newDetailsViewController, nil];

UIViewController <SubstitutableDetailViewController>*vc = (UIViewController <SubstitutableDetailViewController>*)newDetailsViewController;

[vc invalidateRootPopoverButtonItem:_tableViewController.rootPopoverButtonItem];
[_createReportViewController showRootPopoverButtonItem:_tableViewController.rootPopoverButtonItem];

where we have

@protocol SubstitutableDetailViewController
- (void)showRootPopoverButtonItem:(UIBarButtonItem *)barButtonItem;
- (void)invalidateRootPopoverButtonItem:(UIBarButtonItem *)barButtonItem;
@end

the delegate that each of your detailsViewControllers should adhere to. You would implement like this:

- (void)showRootPopoverButtonItem:(UIBarButtonItem *)barButtonItem {
    self.navigationItem.leftBarButtonItem = barButtonItem;
}

- (void)invalidateRootPopoverButtonItem:(UIBarButtonItem *)barButtonItem {
    self.navigationItem.leftBarButtonItem = nil;
}

Let me know if this helps you.

Upvotes: 3

Nekto
Nekto

Reputation: 17877

When you are setting new view controllers placed on navigation stack, probably, all navigation buttons are reset. You can manually add appropriate buttons after changing navigation stack.

For example, you can pick code from - (void)splitViewController: (UISplitViewController*)svc willHideViewController:(UIViewController *)aViewController withBarButtonItem:(UIBarButtonItem*)barButtonItem forPopoverController: (UIPopoverController*)pc where default popover controller button is created:

UIBarButtonItem *item = [[UIBarButtonItem alloc] initWithTitle:@"Menu" style:UIBarButtonItemStyleBordered target:self action:@selector(appropriateSelector)];
[self.navigationItem setLeftBarButtonItem:barButtonItem];
self.popoverController = pc;

Upvotes: 0

Related Questions