navigation in ipads splitviewcontroller. overview create a master-detail application switch device...
TRANSCRIPT
Navigation in iPadssplitViewController
Overview
Create a Master-Detail applicationSwitch Device Family to iPad
Give the project a name and click “Use Storyboards” and “Use Automatic Reference Counting”
Save it somewhere. Do not create a git directory
There’s no nib; instead the storyboard represents several views
The storyboard
Table view for left side (in landscape mode)
splitViewController
Nav controller for right side
Nav controller for left side
View for right side
The left side and right sides (in landscape mode) have separate view and separate view controllers.The right view (called the detail view) is the delegate of the splitViewController.
Organization
SplitViewController
Left side Nav controller
Right side Nav controller
Left side View(table)
(masterViewController)
Right side View (detailViewController)
delegate
You can push new views on the left side
You cannot just push views on the right side; must maintain the delegate relationship.
You can add subviews to the right side without destroying the delegate relationship.
You don’t see this code
Organization
SplitViewController
Left side Nav controller
Left side View(table)
(masterViewController)
The masterview controller has two instance variables that allow it to use the nav controller and spltview controller.
You don’t see these variables in the masterViewController.h file, however, they’re inherited.
self.splitViewController
self.navigationController
delegate
The right side view controller is the delegate of the splitViewController
Why? The right side contains the navigation bar
When the device is in landscape mode, the navigation bar has no button
When the device is in portrait mode, the navigation bar has a button to bring up the popover for navigation.
The splitviewcontroller will call its delegate when the orientation changes to tell the right view when the left view will be hidden.
Classes
You don’t see the code for the splitViewController or for the two nav controllers.
Testing
Run.
Explaining the code
The default master-detail project creates a storyboard splitViewController that includes
A splitViewController (you don’t see the code, just the view in the MainStoryboard)
A navigation controller for the master view (you don’t see the code, just the view in the MainStoryboard)
A master view controller (you see it in the project navigator)
A navigation controller for the detail view (you don’t see the code, just the view in the MainStoryboard)
A detail view controller (you see in the project navigator)
CMPAppDelegate.h
#import <UIKit/UIKit.h>
@interface CMPAppDelegate : UIResponder <UIApplicationDelegate>
@property (strong, nonatomic) UIWindow *window;
@end
Nothing new here.
CMPAppDelegate.m−(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
UISplitViewController *splitViewController = (UISplitViewController *) self.window.rootViewController;
UINavigationController *navigationController = [splitViewController.viewControllers lastObject];
splitViewController.delegate = (id)navigationController.topViewController;
return YES;
}
The splitViewController is by default the rootViewController in this project.
The splitViewController has an array of 2 Navigation Controllers, the first entry is the left view controller, the second the right view controllerThe navigation controller has one
viewcontroller in it’s stack right now; the detail view controller.
Can’t connect the splitViewController delegate to the detail view controller in IB because you cannot make connections between views in a storyboard. You can make segues between views of course.
masterViewController.m
−(void)awakeFromNib
{
self.clearsSelectionOnViewWillAppear = NO;
self.contentSizeForViewInPopover = CGSizeMake(320.0, 600.0);
[super awakeFromNib];
}
Don’t let the table selection disappear when the user comes back to this view.
The popover must be at leaset 320 pixels wide. Otherwise, can make any size.
masterViewController.m−(void)viewDidLoad
{ [super viewDidLoad];
self.navigationItem.leftBarButtonItem = self.editButtonItem;
UIBarButtonItem *addButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(insertNewObject:)]; self.navigationItem.rightBarButtonItem = addButton;
self.detailViewController = (CMPDetailViewController *)[[self.splitViewController.viewControllers lastObject] topViewController];} self.splitViewController.viewControllers lastObject is the navigation
controller for the detail view. The top controller for the navController is the CMPDetailViewController
We’re adding an edit button to the navigation bar.
Now add an insert button.
CMPDetailViewController.h
#import <UIKit/UIKit.h>
@interface CMPDetailViewController : UIViewController <UISplitViewControllerDelegate>
@property (strong, nonatomic) id detailItem;
@property (weak, nonatomic) IBOutlet UILabel *detailDescriptionLabel;
@end
detailItem will hold the information to be displayed.
detailDescriptionLabel will connect to the label in the view.
CMPDetailViewController.m
#import "CMPDetailViewController.h"
@interface CMPDetailViewController ()
@property (strong, nonatomic) UIPopoverController *masterPopoverController;
- (void)configureView;
@end
This is a class extension. masterPopoverController becomes a private data instance.
Similarly, configureView becomes a private method for this class.
CMPDetailViewController.m
− (void)setDetailItem:(id)newDetailItem{ if (_detailItem != newDetailItem) { _detailItem = newDetailItem; // Update the view. [self configureView]; }
if (self.masterPopoverController != nil) { [self.masterPopoverController dismissPopoverAnimated:YES]; } }
−(void)configureView{ // Update the user interface for the detail item.
if (self.detailItem) { self.detailDescriptionLabel.text = [self.detailItem description]; }}
We override the detailItem setter so that we can update the view and dismiss the popover.
If there is a value in the detailItem we set the label in the view to it’s value. Recall that every class has a description method bye default.
CMPDetailViewController.m
− (void)splitViewController:(UISplitViewController *)splitController willHideViewController:(UIViewController *)viewController withBarButtonItem:(UIBarButtonItem *)
barButtonItem forPopoverController:(UIPopoverController *)popoverController{ barButtonItem.title = NSLocalizedString(@"Master", @"Master"); [self.navigationItem setLeftBarButtonItem:barButtonItem animated:YES]; self.masterPopoverController = popoverController;}
− (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. [self.navigationItem setLeftBarButtonItem:nil animated:YES]; self.masterPopoverController = nil;}
These are splitViewController delegate methods.
Create button bar item. Can localize
This method is called when the left side will disappear (iPad has been rotated to portrait mode).The popOverController is passed to us. We need to display this when the button is clicked (done automatically for us).The size of the popOverController was set in “awakeFromNib” method in the masterViewController.
This method is called when the left side will appear (iPad has been rotated to landscape mode).Make button disappear.No longer need the popoverController (the nav table appears on the left side)
Static tables
We usually want to set the information in the navigation table with predetermined data.
We’ll add an array of president and URL to their wikipedia site.
We’ll use a structure called a plist. This is one of Apple’s structures that makes it easy to store and retrieve data.
It’s actually just syntactical sugar over XML
See
Plist
First download the file PresidentList.plist from the class webpage
Reference->examples->PresidentList.plist
Put this into your project You can drag it from the Finder to the project navigator
Make sure the checkbox next to “import” is checked
Part of the PresidentList plist is on the next slide. It creates a dictionary data structure.
The dictionary has one item named presidents
The corresponding value is an array of dictionary entries
plist<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"><plist version="1.0"><dict> <key>presidents</key> <array>
<dict> <key>name</key> <string>George Washington</string> <key>url</key>
<string>http://en.wikipedia.org/wiki/George_Washington</string></dict>
masterViewController.h#import <UIKit/UIKit.h>
@class CMPDetailViewController;
@interface CMPMasterViewController : UITableViewController
@property (strong, nonatomic) CMPDetailViewController *detailViewController;
@property (copy, nonatomic) NSArray *presidents;
@end
Add this property to store the information.
masterViewController.m- (void)viewDidLoad{ [super viewDidLoad];
self.navigationItem.leftBarButtonItem = self.editButtonItem; NSString *path = [[NSBundle mainBundle] pathForResource:@"PresidentList" ofType:@"plist"]; NSDictionary *presidentInfo = [NSDictionary dictionaryWithContentsOfFile:path]; self.presidents = [presidentInfo objectForKey:@"presidents"];
UIBarButtonItem *addButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(insertNewObject:)]; self.navigationItem.rightBarButtonItem = addButton;
self.detailViewController = (CMPDetailViewController *)[[self.splitViewController.viewControllers lastObject] topViewController];}
Add code to initialize the NSArray of Dictionary items for the presidents.
We no longer need this code to insert an insert button.
masterViewController.m
Change these methods:
− (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return _objects.count;
return [self.presidents count];
}
We no longer use the _objects array; instead we use the presidents array
masterViewController.mChange this method:
− (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *) indexPath{ UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath];
NSDate *object = _objects[indexPath.row]; cell.textLabel.text = [object description];
NSDictionary *president = self.presidents[indexPath.row]; cell.textLabel.text = president[@"name"];
return cell;}
We no longer use the _objects array, we now use the presidents array. But note that each entry in the presidents array is a dictionary.
masterViewController.mChange this method:
− (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{ NSDate *object = _objects[indexPath.row]; self.detailViewController.detailItem = object; NSDictionary *president = self.presidents[indexPath.row]; NSString *urlString = president[@"url"]; self.detailViewController.detailItem = urlString;}
We no longer use the _objects array
Each entry in the presidents array is a dictionary with two keys, “name” and “url”. Here we get the value associated with the “url” key.
masterViewController.m
We will no longer have edit or insert buttons, so eliminate these methods from masterViewController.m
− (void)insertNewObject:(id)sender
− (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
− (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
Adding a web view
We can add a web view to actually display the wikipedia web page for a president
We’ll need to change the detailViewController file
Will also need to add the webview to the detailView in the storyboard
detailViewController.h#import <UIKit/UIKit.h>
@interface CMPDetailViewController : UIViewController <UISplitViewControllerDelegate>
@property (strong, nonatomic) id detailItem;
@property (weak, nonatomic) IBOutlet UILabel *detailDescriptionLabel;
@property (weak, nonatomic) IBOutlet UIWebView *webView;
@end
detailViewController.m− (void)configureView{ // Update the user interface for the detail item. NSURL *url = [NSURL URLWithString:self.detailItem]; NSURLRequest *request = [NSURLRequest requestWithURL:url]; [self.webView loadRequest:request];
if (self.detailItem) { self.detailDescriptionLabel.text = [self.detailItem description]; }}
detailViewController.m− (void)splitViewController:(UISplitViewController *)splitController
willHideViewController:(UIViewController *)viewController withBarButtonItem:(UIBarButtonItem *)barButtonItem
forPopoverController:(UIPopoverController *)popoverController{ barButtonItem.title = NSLocalizedString(@"Master", @"Master"); barButtonItem.title = NSLocalizedString(@"Presidents", @"Presidents"); [self.navigationItem setLeftBarButtonItem:barButtonItem animated:YES]; self.masterPopoverController = popoverController;}
Storyboard changes
Go to the storyboard in IB
Find the detail view
Move the label to the top of the view
Change the label to “Select a President”
Get a webview out of the library, make it fill the rest of the space in the detail view
Constrain it left and right and bottom
storyboard
Storyboard
Control-drag from the Detail View Controller icon (in the Detail View Controller − Detail section in the dock, just below the First Responder icon)
to the web view
Choose the webView outlet in the pop-up box
See next slide
Go to the tableView and change the title to Presidents
storyboard
Outlet view webView
Choose webView
Run!