While in portrait mode, the master view controller is missing in action. It would be nice if you could see the master view controller to select a new post from the list without having to rotate the device. UISplitViewController lets you do just that by supplying its delegate with a UIBarButtonItem. Tapping this button shows the master view controller in a UIPopoverController.
In your code, whenever a detail view controller was given to the split view controller, that detail view controller was set as the split view controller’s delegate. When rotating to portrait mode, the detail view controller will get a pointer to the UIBarButtonItem.
In WebViewController.m, implement the following delegate method to place the bar button item in the WebViewController’s navigation item.
- (void)splitViewController:(UISplitViewController *)svc willHideViewController:(UIViewController *)aViewController withBarButtonItem:(UIBarButtonItem *)barButtonItem forPopoverController:(UIPopoverController *)pc { // If this bar button item doesn't have a title, it won't appear at all. [barButtonItem setTitle:@"List"]; // Take this bar button item and put it on the left side of our nav item. [[self navigationItem] setLeftBarButtonItem:barButtonItem]; }
Notice that we explicitly set the title of the button. If the button doesn’t have a title, it won’t appear at all. (If the master view controller’s navigationItem has a title, then the button will be set to that title. But that’s not true in Nerdfeed.)
Build and run the application. Rotate to portrait mode, and you will see the bar button item appear on the left of the navigation bar. Tap that button, and the master view controller’s view will appear in a UIPopoverController.
This bar button item is why we always had you put the detail view controller inside a navigation controller. You don’t have to use a navigation controller to put a view controller in a split view controller, but it makes using the bar button item much easier. (If you don’t use a navigation controller, you can instantiate your own UINavigationBar or UIToolbar to hold the bar button item and add it as a subview of the WebViewController’s view.)
There are three small issues left to address with your List button. First, when the device is rotated back to landscape mode, the button is still there. To remove it, the delegate needs to respond to another message from the UISplitViewController. Implement this delegate method in WebViewController.m.
- (void)splitViewController:(UISplitViewController *)svc willShowViewController:(UIViewController *)aViewController invalidatingBarButtonItem:(UIBarButtonItem *)button { // Remove the bar button item from our navigation item // We'll double check that its the correct button, even though we know it is if (button == [[self navigationItem] leftBarButtonItem]) [[self navigationItem] setLeftBarButtonItem:nil]; }
Build and run the application. The List button will now appear and disappear as you rotate between portrait and landscape modes.
The second issue is that the ChannelViewController also needs to show the List button. In ChannelViewController.m, implement the two UISplitViewControllerDelegate methods.
- (void)splitViewController:(UISplitViewController *)svc willHideViewController:(UIViewController *)aViewController withBarButtonItem:(UIBarButtonItem *)barButtonItem forPopoverController:(UIPopoverController *)pc { [barButtonItem setTitle:@"List"]; [[self navigationItem] setLeftBarButtonItem:barButtonItem]; } - (void)splitViewController:(UISplitViewController *)svc willShowViewController:(UIViewController *)aViewController invalidatingBarButtonItem:(UIBarButtonItem *)button { if (button == [[self navigationItem] leftBarButtonItem]) [[self navigationItem] setLeftBarButtonItem:nil]; }
Build and run the application. Now the List button will also appear on the navigation bar when the ChannelViewController is on the screen.
Since both WebViewController and ChannelViewController can be the delegate for a UISplitViewController, it’s best to declare that they conform to the UISplitViewControllerDelegate protocol.
In WebViewController.h, add this declaration:
@interface WebViewController : UIViewController <ListViewControllerDelegate, UISplitViewControllerDelegate>
And do the same in ChannelViewController.h.
@interface ChannelViewController : UITableViewController <ListViewControllerDelegate, UISplitViewControllerDelegate>
Build and run the application. The behavior will be the same, but there won’t be any warnings.
Finally, if you are in portrait mode and switch between the ChannelViewController and the WebViewController, the List button disappears. To keep the button on the screen, the ListViewController needs to take the button from the current detail view controller and give it to the new detail view controller.
At the top of ListViewController.m, create a category to implement a new private method:
@interface ListViewController () - (void)transferBarButtonToViewController:(UIViewController *)vc; @end @implementation ListViewController
Then, implement this method in ListViewController.m.
- (void)transferBarButtonToViewController:(UIViewController *)vc { // Get the navigation controller in the detail spot of the split view controller UINavigationController *nvc = [[[self splitViewController] viewControllers] objectAtIndex:1]; // Get the root view controller out of that nav controller UIViewController *currentVC = [[nvc viewControllers] objectAtIndex:0]; // If it's the same view controller, let's not do anything if (vc == currentVC) return; // Get that view controller's navigation item UINavigationItem *currentVCItem = [currentVC navigationItem]; // Tell new view controller to use left bar button item of current nav item [[vc navigationItem] setLeftBarButtonItem:[currentVCItem leftBarButtonItem]]; // Remove the bar button item from the current view controller's nav item [currentVCItem setLeftBarButtonItem:nil]; }
Whenever the user switches between the two different views, you will invoke this method. Because this method references the current detail view controller, it must be called before the split view controller is updated with a new set of view controllers. In ListViewController.m, invoke this method near the top of showInfo:.
- (void)showInfo:(id)sender { ChannelViewController *channelViewController = [[[ChannelViewController alloc] initWithStyle:UITableViewStyleGrouped] autorelease]; if ([self splitViewController]) { [self transferBarButtonToViewController:channelViewController]; UINavigationController *nvc = [[UINavigationController alloc] initWithRootViewController:channelViewController];
Now do the same going the other way. In ListViewController.m, locate the tableView:didSelectRowAtIndexPath: method and add the following code:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { if (![self splitViewController]) [[self navigationController] pushViewController:webViewController animated:YES]; else { [self transferBarButtonToViewController:webViewController]; UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:webViewController];
Build and run the application. The List button should now always appear in portrait mode – no matter what the user does – and never appear in landscape mode.
18.118.9.197