Taking pictures and UIImagePickerController
Now you need a button to initiate the photo-taking process. It would be nice put this button on the navigation bar, but we will need the navigation bar for another button later. Instead, we will create an instance of UIToolbar and place it at the bottom of ItemDetailViewController's view.
A UIToolbar works lot like a UINavigationBar in that you can add UIBarButtonItems to it. However, where a navigation bar has two bar button items, a toolbar has an array of items. You can place as many UIBarButtonItems in a toolbar as can fit on the screen.
By default, a new instance of UIToolbar create in Interface Builder comes with one UIBarButtonItem. Select this bar button item and open the attribute inspector. Change the Identifier to Camera, and the item will show a camera icon.
- (void)takePicture:(id)sender{ UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init]; // If our device has a camera, we want to take a picture, otherwise, we // just pick from photo library if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) { [imagePicker setSourceType:UIImagePickerControllerSourceTypeCamera]; } else { [imagePicker setSourceType:UIImagePickerControllerSourceTypePhotoLibrary]; } // This line of code will generate 2 warnings right now, ignore them [imagePicker setDelegate:self]; // Place image picker on the screen [self presentModalViewController:imagePicker animated:YES]; // The image picker will be retained by ItemDetailViewController // until it has been dismissed [imagePicker release];}
When image's picked, delegate handler
- (void)imagePickerController:(UIImagePickerController *)pickerdidFinishPickingMediaWithInfo:(NSDictionary *)info{ // Get picked image from info dictionary UIImage *image = [info objectForKey:UIImagePickerControllerOriginalImage]; // Put that image onto the screen in our image view [imageView setImage:image]; // Take image picker off the screen - // you must call this dismiss method [self dismissModalViewControllerAnimated:YES];}
To get around this problem, we must create a separate store for images. Instead of putting the image
directly into the imageView, we will put it into this store. Then when the ItemDetailViewController’sview next appears on screen, we’ll have the ItemDetailViewController grab the image from theimage store and put it into its own imageView. In general, this is a best practice: a view controllershould re-populate its view’s subviews with data whenever it is sent the message viewWillAppear:,eliminating the possibility that a low-memory warning could wipe out its content.- (void)imagePickerController:(UIImagePickerController *)pickerdidFinishPickingMediaWithInfo:(NSDictionary *)info{ NSString *oldKey = [possession imageKey]; // Did the possession already have an image? if (oldKey) { // Delete the old image [[ImageStore defaultImageStore] deleteImageForKey:oldKey]; } UIImage *image = [info objectForKey:UIImagePickerControllerOriginalImage]; // Create a CFUUID object - it knows how to create unique identifier strings CFUUIDRef newUniqueID = CFUUIDCreate (kCFAllocatorDefault); // Create a string from unique identifier CFStringRef newUniqueIDString = CFUUIDCreateString (kCFAllocatorDefault, newUniqueID); // Use that unique ID to set our possessions imageKey [possession setImageKey:(NSString *)newUniqueIDString]; // We used "Create" in the functions to make objects, we need to release them CFRelease(newUniqueIDString); CFRelease(newUniqueID); // Store image in the ImageStore with this key [[ImageStore defaultImageStore] setImage:image forKey:[possession imageKey]]; // Put that image onto the screen in our image view [imageView setImage:image]; // Take image picker off the screen [self dismissModalViewControllerAnimated:YES];}
and in viewWillAppear:
- (void)viewWillAppear:(BOOL)animated{ [super viewWillAppear:animated]; [nameField setText:[possession possessionName]]; [serialNumberField setText:[possession serialNumber]]; [valueField setText:[NSString stringWithFormat:@"%d", [possession valueInDollars]]]; NSDateFormatter *dateFormatter = [[[NSDateFormatter alloc] init] autorelease]; [dateFormatter setDateStyle:NSDateFormatterMediumStyle]; [dateFormatter setTimeStyle:NSDateFormatterNoStyle]; [dateLabel setText: [dateFormatter stringFromDate:[possession dateCreated]]]; [[self navigationItem] setTitle:[possession possessionName]]; NSString *imageKey = [possession imageKey]; if (imageKey) { // Get image for image key from image store UIImage *imageToDisplay = [[ImageStore defaultImageStore] imageForKey:imageKey]; // Use that image to put on the screen in imageView [imageView setImage:imageToDisplay]; } else { // Clear the imageView [imageView setImage:nil]; }}
Dismissing the keyboard
When the keyboard appears on the screen in the possession detail view, it obscures imageView. This is annoying.
to dismiss the keyboard, conform to UITextFieldDelegate protocol
@interface ItemDetailViewController : UIViewController//In ItemDetailViewController.m, implement textFieldShouldReturn:.- (BOOL)textFieldShouldReturn:(UITextField *)textField{ [textField resignFirstResponder]; return YES;}
UIControl
We have seen how classes like UIButton can send an action message to a target when tapped. Buttons
inherit this target-action behavior from their superclass, UIControl. You’re going to change the viewof ItemDetailViewController from an instance of UIView to an instance of UIControl so that it canhandle touch events.select the main view instance. Open the identity inspector and change the view’s class to UIControl.
choose
Connection: Action
Event: Touch Up Inside
- (IBAction)backgroundTapped:(id)sender{ [[self view] endEditing:YES];}