Advice on getting multiple images display and store (Chap 12


#1

I am hoping someone can shed some lights for me. I am on Chapter 12 … the Camera and I’d like to get multiple Image view … one from the camera and the other image from doing barcode scanning. How should I do this - e.g. data wise - BNRImageStore - can I extend this and how?

Thanks


#2

Thanks for this question. This took this as the Platinum Challenge for this chapter.

For this I added a NSMutableArray to the BNRItem to hold multiple keys (alloc init added for this array in the designated initiator of BNRItem.m). No need to change the BNRImageStore as the keys themselves do not change.
Add several methods to the BNRItem, I created:

[code]- (NSString *)getImageKey:(NSInteger)i;

  • (void)removeImageKey:(NSString *)s;
  • (void)addImageKey:(NSString *)s;
  • (NSInteger)lastImage;[/code]

Doing:

[code]- (NSString *)getImageKey:(NSInteger)i
{
NSString *returnString = nil;

if (i <= [self lastImage]) {
    returnString = [imageArray objectAtIndex:i];
}

return returnString;

}

  • (void)removeImageKey:(NSString *)s
    {
    [imageArray removeObject:s];
    }

  • (void)addImageKey:(NSString *)s
    {
    [imageArray addObject:s];
    }

  • (NSInteger)lastImage
    {
    return [imageArray count] - 1;
    }
    [/code]

Then I had to add some code to the DetailViewController to manage (add, delete, switch) the multiple pictures.
I declared a NSInteger in the DetailViewController.h to hold the current image number displayed

- (void)viewDidLoad { [super viewDidLoad]; [[self view] setBackgroundColor:[UIColor groupTableViewBackgroundColor]]; currentImageNumber = 0; }

In the .xib I added two additional buttons in the tool bar, one with “<<” and one with “>>”

And linked them with

[code]- (IBAction)previousPicture:(id)sender;

  • (IBAction)nextPicture:(id)sender;[/code]

[code]- (IBAction)previousPicture:(id)sender
{
if (!currentImageNumber == 0) {
currentImageNumber -= 1;
[self setImage];
}
}

  • (IBAction)nextPicture:(id)sender
    {
    if (currentImageNumber < [item lastImage]) {
    currentImageNumber += 1;
    [self setImage];
    }
    }
    [/code]

Realising that the UIView had to be set at multiple places I created a method just for this

[code]- (void)setImage
{
NSString *imageKey = [item getImageKey:currentImageNumber];
if (imageKey) {
//get image for imageKey from image store
[imageView setImage:[[BNRImageStore sharedStore] imageForKey]];

    [imageButton setEnabled:YES];
} else {
    //clear the imageView
    [imageView setImage:nil];
    
    [imageButton setEnabled:NO];
}

}[/code]

Then in all the places where the original coding refers to the [item imageKey] you have to make some replacements

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear];
    
    [nameField setText:[item itemName]];
    [serialNumberField setText:[item serialNumber]];
    [valueField setText:[NSString stringWithFormat:@"%d", [item valueInDollars]]];
    
    //create a NSDateFormatter that will turn a date into a simple date string
    NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
    [dateFormatter setDateStyle:NSDateFormatterMediumStyle];
    [dateFormatter setTimeStyle:NSDateFormatterNoStyle];
    
    //use filtered NSDate object to set dateLabel contents
    [dateLabel setText:[dateFormatter stringFromDate:[item dateCreated]]];
    
    [self setImage];
}

- (IBAction)deletePicture:(id)sender
{
    //delete the image
    NSString *deleteKey = [item getImageKey:currentImageNumber];
    [[BNRImageStore sharedStore] deleteImageForKey:deleteKey];
    [item removeImageKey:deleteKey];
    
    //if there are more images show the previous image, otherwise clear the view
    if (!currentImageNumber == 0) {
        currentImageNumber -= 1;
    }
    [self setImage];
}

- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
    
    //MULTIPLE IMAGES, NO LONGER NEEDED TO CLEAN THE OLD KEY/IMAGE HERE
    //NSString *oldKey = [item getImageKey:currentImageNumber];
    
    //did the item allready have an image?
    //if (oldKey) {
    //    //delete the image
    //    [[BNRImageStore sharedStore] deleteImageForKey:oldKey];
    //    [item removeImageKey:oldKey];
    //}
    
    //get picked image from info dictionary
    UIImage *image = [info objectForKey:UIImagePickerControllerEditedImage];

    //create a CFUUID object - it knows how to create unique identifier strings
    CFUUIDRef newUniqueID = CFUUIDCreate(kCFAllocatorDefault);
    
    //create a string from the unique identifier
    CFStringRef newUniqueIDString = CFUUIDCreateString(kCFAllocatorDefault, newUniqueID);
    
    //use that unique ID to set our imageKey
    NSString *key = (__bridge NSString *)newUniqueIDString;
    [item addImageKey:key];
    
    //MULTIPLE IMAGES
    //the image is added as the last, so to show the last image the currentImageNumber must be set to the last
    currentImageNumber = [item lastImage];
    
    //store the image in the sharedstore
    [[BNRImageStore sharedStore] setImage:image forKey:[item getImageKey:currentImageNumber]];
    
    //destroy Core Foundation objects
    CFRelease(newUniqueID);
    CFRelease(newUniqueIDString);
    
    [self setImage];
    
    //take image picker off the screen - you must call this dismiss method
    [self dismissViewControllerAnimated:YES completion:nil];
}

And it works!


#3

that solution worked really well!

I have a question based on that though: how would that be converted to use Core Data? I know that’s a few chapters ahead but I’ve finished it and noticed this breaks the core data model for saving the images. I’ve tried declaring imageArray to be an NSMutableSet as a one-to-many relationship but making that a dynamic property in BNRItem breaks the new methods in BNRItem for removing/adding image keys and lastImage. Is there a way to read out a mutable array into an coredata model, somewhere in the itemstore? I’ve googled for a few hours and read the documentation but I’m still struggling with core data. Sorry if this should go in the forum specific to core data; I figured it fit better here since it’s specific to this method.

edit: one error I did find today with fresh eyes is that new items appear with the same images as the previous item, so instead of multiple images for one it seems that there is just one large library of images shared by all BNRItems.