Assignment #6 Task #7

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

To make all this work, of course, you will need to add a Visit/Unvisit button to the scenes in the storyboard which show a photo. If that photo is already in “My Vacation,” then the button’s title should appear as “Unvisit,” otherwise it should appear as “Visit.” Clicking this button toggles whether the photo is part of “My Vacation” or not.

Add a new property which holds the “Visit/Unvisit” button and gets lazy initialized in its getter:

@property (nonatomic, strong) IBOutlet UIBarButtonItem *visitButton;

@synthesize visitButton = _visitButton;

- (UIBarButtonItem *)visitButton
{
    if (!_visitButton) {
        _visitButton = [[UIBarButtonItem alloc] initWithTitle:@"Visit"
                                                        style:UIBarButtonItemStyleBordered 
                                                       target:self 
                                                       action:@selector(visitButtonPressed:)];
    }
    return _visitButton;
}


To display this button, the iPhone and the iPad have to be handled differently. While the iPhone has a navigation bar, the iPhone uses a toolbar. The text depends on whether the photo is part of the current vacation:

- (void)displayVisitButtonForPhotoID:(NSString *)photoID {
    VacationHelper *vh = [VacationHelper sharedVacation:nil];
    [VacationHelper openVacation:vh.vacation usingBlock:^(BOOL success) {
        if ([Photo exisitingPhotoWithID:photoID 
                inManagedObjectContext:vh.database.managedObjectContext]) {
            self.visitButton.title = @"Unvisit";
        } else {
            self.visitButton.title = @"Visit";
        }
    }];    
    if (self.splitViewController) {
        NSMutableArray *toolbarItems = [self.toolbar.items mutableCopy];
        [toolbarItems addObject:self.visitButton];
        self.toolbar.items = toolbarItems;                
    } else {
        self.navigationItem.rightBarButtonItem = self.visitButton;
    }
}

- (void)hideVisitButton {
    if (self.splitViewController) {
        NSMutableArray *toolbarItems = [self.toolbar.items mutableCopy];
        [toolbarItems removeObject:self.visitButton];
        self.toolbar.items = toolbarItems;                        
    } else {
        self.navigationItem.rightBarButtonItem = nil;
    }            
}

When a photo has been loaded the button will be displayed otherwise it is hidden:

- (void)loadPhoto
{
....
    [self hideVisitButton];
    if (self.photo || self.coreDataPhoto) [self.spinner startAnimating];
....
            [self.spinner stopAnimating];
            [self displayVisitButtonForPhotoID:photoID];
....
}

When the button gets pressed and the user is currently “at vacation” the photo will be removed from the vacation. Thus on the iPhone we pop to the previous screen, on the iPad we make the photo opaque. When not on vacation the action of the button depends on whether the photo is part of a vacation and thus gets added or removed.

- (IBAction)visitButtonPressed:(id)sender
{
    VacationHelper *vh = [VacationHelper sharedVacation:nil];
    if (self.coreDataPhoto) {
        if (self.splitViewController) {
            self.imageView.alpha = 0.5;
            [self hideVisitButton];            
        } else {
            [self.navigationController popViewControllerAnimated:YES];
        }
        [Photo removePhoto:self.coreDataPhoto];
        [vh.database saveToURL:vh.database.fileURL 
              forSaveOperation:UIDocumentSaveForOverwriting 
             completionHandler:NULL];
    } 
    if (self.photo) {
        if ([self.visitButton.title isEqualToString:@"Visit"]) {
            self.visitButton.title = @"Unvisit";
            [VacationHelper openVacation:vh.vacation usingBlock:^(BOOL success) {
                [Photo photoFromFlickrInfo:self.photo 
                    inManagedObjectContext:vh.database.managedObjectContext];
            }];            
        } else {
            self.visitButton.title = @"Visit";
            [VacationHelper openVacation:vh.vacation usingBlock:^(BOOL success) {
                [Photo removePhotoWithID:[self.photo objectForKey:FLICKR_PHOTO_ID] 
                  inManagedObjectContext:vh.database.managedObjectContext];
            }];
        }
        [vh.database saveToURL:vh.database.fileURL 
              forSaveOperation:UIDocumentSaveForOverwriting 
             completionHandler:NULL];
    }
}

Saving the database after each change is not strictly necessary in the code above as Core Data includes an auto-save functionality.

For checking if a photo is in the database and removing it we need to expand the Photo (Flickr) category. Searching for an photo ID is a simple database query. Deleting a photo needs to make sure to update tags and places, as they have to be deleted when the last photo referencing to them gets deleted. In addition the photo count of the tags needs to be adjusted:

+ (Photo *)exisitingPhotoWithID:(NSString *)photoID 
        inManagedObjectContext:(NSManagedObjectContext *)context
{
    Photo *photo;
    NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Photo"];
    request.predicate = [NSPredicate predicateWithFormat:@"unique = %@", photoID];
    request.sortDescriptors = [NSArray arrayWithObject:
                               [NSSortDescriptor sortDescriptorWithKey:@"title" 
                                                             ascending:YES]];
    NSError *error;
    NSArray *matches = [context executeFetchRequest:request error:&error];
    if (!matches || ([matches count] > 1 || error)) {
        NSLog(@"Error in exisitingPhotoForID: %@ %@", matches, error);
    } else if ([matches count] == 0) {
        photo = nil;
    } else {
        photo = [matches lastObject];
    }
    return photo;    
}

+ (void)removePhoto:(Photo *)photo
{
    NSManagedObjectContext *context = photo.managedObjectContext;    
    for (Tag *tag in photo.tags) {
        if ([tag.photos count] == 1) [context deleteObject:tag];
        else tag.count = [NSNumber numberWithInt:[tag.photos count] - 1];
    }    
    if ([photo.place.photos count] == 1) [context deleteObject:photo.place];        
    [context deleteObject:photo];    
}

+ (void)removePhotoWithID:(NSString *)photoID
   inManagedObjectContext:(NSManagedObjectContext *)context
{
    [Photo removePhoto:[Photo exisitingPhotoWithID:photoID inManagedObjectContext:context]];
}

Finally the VacationHelper needs a minor adjustment to make sure if no database has been selected before it is set to the default one:

+ (void)openVacation:(NSString *)vacationName usingBlock:(void (^)(BOOL))block
{
    VacationHelper *vh = [VacationHelper sharedVacation:vacationName];
    if (!vacationName && !vh.vacation) vh.vacation = DEFAULT_VACATION_NAME;
....

The complete code for this task is available at github.

FacebooktwitterredditpinterestlinkedintumblrmailFacebooktwitterredditpinterestlinkedintumblrmail

Leave a Reply

Your email address will not be published.