cs193p – Assignment #4 Extra Task #2

Please note, this blog entry is from a previous course. You might want to check out the current one.

Make your application work on the iPad as well with appropriate user-interface idioms on that platform. This will require you to have absorbed the lecture immediately after this was assigned. But you’ll be asked to do this next week anyway, so you’ll be ahead of the game if you do it this week (and get a little bit of extra credit for doing it early).

In the iPad storyboard remove the default controller. Drag out a new split-view controller. Remove the default master and detail views. Go to the iPhone storyboard copy every thing and paste it to the Pad storyboard. Set the tab-view controller to be the master view controller. Remove the segues pointing to the image view controller and set the image view as detail view controller:

cs193p - assignment #4 extra task #2
cs193p – assignment #4 extra task #2


Note that we do not use replace segues like in shutter bug because we need a controller which is always available and can handle the show-master-view button in portrait mode. If we would use a replace segue, we would need to hand over this button from one detail view controller to the next one.

Add a tool bar as well as a bar button as title to the image view controller and create outlets for them:

@property (weak, nonatomic) IBOutlet UIBarButtonItem *titleBarButtonItem;
@property (weak, nonatomic) IBOutlet UIToolbar *toolbar;

Use the setter for the title property to set also the tool-bar title:

@property (weak, nonatomic) IBOutlet UIBarButtonItem *titleBarButtonItem;
@property (weak, nonatomic) IBOutlet UIToolbar *toolbar;

Declare the image view controller to be the a split-view-controller delegate and create another property to hold the split-view bar button (which will appear and disappear depending on if we are in portrait or landscape mode):

@interface ImageViewController () <..., UISplitViewControllerDelegate>
...
@property (nonatomic, strong) UIBarButtonItem *splitViewBarButtonItem;

When the master view controller will appear, remove the button:

- (void)splitViewController:(UISplitViewController *)svc
     willShowViewController:(UIViewController *)aViewController
  invalidatingBarButtonItem:(UIBarButtonItem *)barButtonItem
{
    self.splitViewBarButtonItem = nil;
}

When it is not on screen create a button:

- (void)splitViewController:(UISplitViewController *)svc
     willHideViewController:(UIViewController *)aViewController
          withBarButtonItem:(UIBarButtonItem *)barButtonItem
       forPopoverController:(UIPopoverController *)pc
{
    UIBarButtonItem *button = [[UIBarButtonItem alloc]
                               initWithBarButtonSystemItem:UIBarButtonSystemItemSearch
                               target:barButtonItem.target
                               action:barButtonItem.action];
    barButtonItem = button;
    self.splitViewBarButtonItem = barButtonItem;
}

… where you actually only need to do something it the button changed:

- (void)setSplitViewBarButtonItem:(UIBarButtonItem *)splitViewBarButtonItem
{
    if (_splitViewBarButtonItem != splitViewBarButtonItem) {
        [self handleSplitViewBarButtonItem:splitViewBarButtonItem];
    }
}

… which actually is removing any old button, and placing the new one on the first position of the tool bar:

- (void)handleSplitViewBarButtonItem:(UIBarButtonItem *)splitViewBarButtonItem
{
    NSMutableArray *toolbarItems = [self.toolbar.items mutableCopy];
    if (_splitViewBarButtonItem) {
        [toolbarItems removeObject:_splitViewBarButtonItem];
    }
    if (splitViewBarButtonItem) {
        [toolbarItems insertObject:splitViewBarButtonItem atIndex:0];
    }
    self.toolbar.items = toolbarItems;
    _splitViewBarButtonItem = splitViewBarButtonItem;
}

Finally we need to setup everything when the view has been loaded:

- (void)viewDidLoad
{
    ...
    self.titleBarButtonItem.title = self.title;
    self.splitViewController.delegate = self;
    [self handleSplitViewBarButtonItem:self.splitViewBarButtonItem];
}

Previously we only recalculated the zoom factor when the sub view has been changed – which it did when the view appeared on screen or the screen had been rotated. Because it now stays on screen permanently, move that particular part of the code into a new helper method and call it also when the image has been changed:

- (void)setZoomScaleToFillScreen
{
    double wScale = self.scrollView.bounds.size.width / self.imageView.image.size.width;
    double hScale = self.scrollView.bounds.size.height / self.imageView.image.size.height;
    if (wScale > hScale) self.scrollView.zoomScale = wScale;
    else self.scrollView.zoomScale = hScale;
}

- (void)resetImage
{
    if (self.scrollView) {
        ...
        if (image) {
            ...
            [self setZoomScaleToFillScreen];
        }
    }
}

- (void)viewDidLayoutSubviews
{
    [super viewDidLayoutSubviews];
    [self setZoomScaleToFillScreen];
}

Because we do not use a segue any more (at least for the iPad – for the iPhone we still do), move the code where the data for the image is set into another helper function, and call this function also when a table cell has been selected:

- (void)sendDataforIndexPath:(NSIndexPath *)indexPath toViewController:(UIViewController *)vc
{
    if ([vc respondsToSelector:@selector(setImageURL:)]) {
        [RecentFlickrPhotos addPhoto:self.photos[indexPath.row]];
        NSURL *url = [FlickrFetcher urlForPhoto:self.photos[indexPath.row] format:FlickrPhotoFormatLarge];
        [vc performSelector:@selector(setImageURL:) withObject:url];
        [vc setTitle:[self titleForRow:indexPath.row]];
    }
}

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    if ([sender isKindOfClass:[UITableViewCell class]]) {
        NSIndexPath *indexPath = [self.tableView indexPathForCell:sender];
        if (indexPath) {
            if ([segue.identifier isEqualToString:@"Show Image"]) {
                [self sendDataforIndexPath:indexPath
                          toViewController:segue.destinationViewController];
            }
        }
    }
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    [self sendDataforIndexPath:indexPath
              toViewController:[self.splitViewController.viewControllers lastObject]];
}

The complete code is available on github.

Facebooktwittergoogle_plusredditpinterestlinkedintumblrmailFacebooktwittergoogle_plusredditpinterestlinkedintumblrmail

Flattr this!

Leave a Reply

Your email address will not be published. Required fields are marked *