Assignment #6 Extra Task #1

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

Add UI to allow the user to add new vacation documents and to pick which vacation they are visiting when they choose the Visit button.

For the first part of this task add a new view controller to both storyboards and setup a new subclass of the UIViewController class. Add a bar button item to the navigation item of the vacations table view controller with a push segue for the iPhone and a popover segue for the iPad from this new button to the new view controller. Add a text field with an outlet and a button with an action to the view controller.

To be able to close the popover controller after the button has been pressed, it is necessary to delegate this to the vacations view controller because it also creates the popover. Thus we add a protocol for the delegate:

@protocol AddVacationViewControllerDelegate <NSObject>
- (void)addVacationViewController:(AddVacationViewController *)sender 
                    addedVacation:(NSString *)vacation;
@property (nonatomic, weak) id <AddVacationViewControllerDelegate> delegate;

When the button gets pressed, we check if the entry is valid. If the vacation does already exist we send an alert to the user. Otherwise we create the new vacation by saving it and call the delegate to close the current view:

- (IBAction)addVacationName {
    NSString *name = self.vacationName.text;    
    if ([name isEqualToString:@""]) return;    
    VacationHelper *vh = [VacationHelper sharedVacation:name];    
    if ([vh.fileManager fileExistsAtPath:[vh.database.fileURL path]]) {
        UIAlertView *alert = [[UIAlertView alloc] 
                                     message:@"this vacation does already exist." 
        [alert show];
    [vh.database saveToURL:vh.database.fileURL 
         completionHandler:^(BOOL success) {
             [self.delegate addVacationViewController:self addedVacation:name];

The vacations view controller needs to set itself as delegate and if running on an iPad save a pointer to the popover controller.

@interface VacationsTableViewController () <AddVacationViewControllerDelegate>
@property (nonatomic, strong) UIPopoverController *popoverController; 
@synthesize popoverController;
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
    if ([segue.identifier isEqualToString:@"Add Vacation"]) {
        if ([segue isKindOfClass:[UIStoryboardPopoverSegue class]]) {
            UIStoryboardPopoverSegue *popoverSegue = (UIStoryboardPopoverSegue *) segue;
            [self.popoverController dismissPopoverAnimated:YES];
            self.popoverController = popoverSegue.popoverController;
        [segue.destinationViewController setDelegate:self];

The delegated methods removes the view controller for the iPhone and closes the popover controller for the iPad. Because a vacation has been added, the table of the vacations view controller needs to be reloaded:

- (void)addVacationViewController:(AddVacationViewController *)sender 
                    addedVacation:(NSString *)vacation
    [self.navigationController popViewControllerAnimated:YES];
    [self.popoverController dismissPopoverAnimated:YES];
    self.popoverController = nil; = [VacationHelper getVacations];
    [self.tableView reloadData];

The second part of the task could look similar but as we have previously created the “Visit/Unvisit” button in code, we will try to do this part also in code.

When a table view controller comes from a storyboard it includes a couple of setups which need be added manually when setup via code. The photo view controller will provide the functionality of a table view controller thus it has to act as delegate and data source:

@interface PhotoViewController () <...,

The model for the table is an array of available vacations which only needs to be populated when a photo is available:

@property (nonatomic, strong) NSArray *vacations;
@synthesize vacations = _vacations;
- (void)setPhoto:(NSDictionary *)photo
... = [VacationHelper getVacations];

And is used to create the table data using the usual methods:

- (NSInteger)tableView:(UITableView *)tableView 
 numberOfRowsInSection:(NSInteger)section {    
    return [ count];

- (UITableViewCell *)tableView:(UITableView *)tableView 
         cellForRowAtIndexPath:(NSIndexPath *)indexPath {    
    static NSString *CellIdentifier = @"Visit Vacations Cell";    
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];    
    if (!cell)        
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault 
    cell.textLabel.text = [ objectAtIndex:indexPath.row];    
    return cell;    

When the visit button gets pressed and we have a photo which is not already in the current vacation, we create the table view, setup the delegate and the data source an place it when on an iPad inside a popover controller, and when on an iPhone we push it on the stack of the navigation controller.

- (IBAction)visitButtonPressed:(id)sender
    if ( {
        if ([self.visitButton.title isEqualToString:@"Visit"]) {
            CGRect frame = CGRectMake(0, 0, 
            UITableView *tableView = [[UITableView alloc] 
            tableView.autoresizingMask = UIViewAutoresizingFlexibleHeight 
                                         | UIViewAutoresizingFlexibleWidth;            
            tableView.delegate = self;            
            tableView.dataSource = self;            
            [tableView reloadData];
            UIViewController* content = [[UIViewController alloc] init];
            content.view = tableView;
            if (self.splitViewController) {
                self.visitButton.enabled = NO;            
                CGSize size = CGSizeMake(VACATION_SELECTION_POPOVER_TABLE_SIZE, 
                content.contentSizeForViewInPopover = size;
                UIPopoverController* popover = [[UIPopoverController alloc]
                popover.delegate = self;
                self.popoverController = popover;
                [self.popoverController presentPopoverFromBarButtonItem:sender
            } else {
                [self.navigationController pushViewController:content animated:YES];
        } else {

Like before we need a way to access the popover controller:

@interface PhotoViewController () <..., 
@property (nonatomic, strong) UIPopoverController *popoverController;
@synthesize popoverController;

Finally we need to save the photo into the chosen vacation database, pop back to the previous view when on the iPhone or close the popover controller when on the iPad.

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
    VacationHelper *vh = [VacationHelper sharedVacation:
                          [ objectAtIndex:indexPath.row]];
    [VacationHelper openVacation:vh.vacation usingBlock:^(BOOL success) {
        [self.navigationController popViewControllerAnimated:YES];
        [self.popoverController dismissPopoverAnimated:YES];
        self.popoverController = nil;
        self.visitButton.title = @"Unvisit";
        self.visitButton.enabled = YES;

The complete code for this task is available at github.


Flattr this!

Leave a Reply

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