Basic Security in iOS 5 – Part 2

This is a post by Chris Lowe, an iOS application developer and aspiring game developer living in San Antonio, Texas. Welcome to Part Two of the epic tutorial series on implementing basic security in iOS 5! In Part One, we began work on Christmas Keeper, an app designed to keep track of holiday gift ideas. […] By Chris Lowe.

Leave a rating/review
Save for later
Share
You are currently viewing page 3 of 4 of this article. Click here to view the first page.

Creating a Custom Cell

From here, we’ll detour to create a table cell that we are going to use to help simplify our custom table cell code.

Create a new file with the iOS\Cocoa Touch\Objective-C class template, name the class ChristmasListTableViewCell and make it a subclass of UITableViewCell. All you need to do here is add the following properties to ChristmasListTableViewCell.h:

@property (nonatomic, strong) IBOutlet UIImageView *thumbnail;
@property (nonatomic, strong) IBOutlet UILabel *textView;

Then synthesize them in ChristmasListTableViewCell.m:

@synthesize thumbnail;
@synthesize textView;

And that’s it!

Let’s take a break from code for a minute and hook up this class with our UITableViewCell inside of our Christmas Presents Table View.

Head back to MainStoryboard.storyboard and select the cell inside of the Table View called “Christmas Keeper” – I find it easiest to click just to the right of the Disclosure Indicator. Then bring up the Identity Inspector and change the “Class” property to be our newly created ChristmasListTableViewCell class.

CKClassCell

Then Control+Drag from the Table View Cell to the UILabel and to the UIImageView to hook up the outlets we made. Everyone does this their own way, but I find it easy to do with the Document Outline (the view to the left of the Storyboard).

CKDocOutline

Build and run real quick just to make sure everything still runs (although it still won’t display any presents quite yet).

It Giveth Presents, and it Taketh Them Away

Now we need to create two more view controllers – one to handle adding new presents and another to show the presents.

Create a new file with the iOS\Cocoa Touch\UIViewController subclass template, enter AddChristmasItemViewController for the class, make it a subclass of UITableViewController, and leave both checkboxes unchecked.

Replace AddChristmasItemViewController.h with the following, to define a protocol to pass back the new present details and define a few properties that we’ll hook up in a bit:

#import 

// This delegate is used to send back the newly created present to the Table View.
@protocol AddChristmasItemDelegate 
-(void)addChristmasItemToList:(NSDictionary *)item;
@end

@interface AddChristmasItemViewController : UITableViewController 

@property (nonatomic, strong) IBOutlet UITextView *presentText;
@property (nonatomic, strong) IBOutlet UIImageView *presentImage;
@property (nonatomic, strong) UIImagePickerController *imagePicker;
@property (nonatomic, strong) NSString *presentImageFileName;
@property (nonatomic, weak) id delegate;

-(IBAction)cancel:(id)sender;
-(IBAction)done:(id)sender;

@end

Then switch to AddChristmasItemViewController.m and replace it with the following:

#import "AddChristmasItemViewController.h"

@implementation AddChristmasItemViewController
@synthesize delegate, presentText, presentImage, imagePicker, presentImageFileName;

- (void)presentPickerForPhoto 
{
    
    // Set source to the Photo Library (so it works in Simulator and on non-camera devices).
    self.imagePicker.sourceType =  UIImagePickerControllerSourceTypePhotoLibrary;
    
    // Set delegate.
    self.imagePicker.delegate = self;
    
    // Disable image editing.
    self.imagePicker.allowsEditing = NO;
    
    // Show image picker.
    [self presentModalViewController:self.imagePicker animated:YES];
}

// Once the user has made a selection, the delegate call back comes here.
- (void) imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
    [picker dismissModalViewControllerAnimated:YES];
    
    // Access the original image from info dictionary.
    UIImage *image = [info objectForKey:@"UIImagePickerControllerOriginalImage"];
    [self.presentImage setImage:image];
    
    // Capture the file name of the image.
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];
    NSError *error = nil;
    NSArray *dirContents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:documentsDirectory error:&error];
    NSString *fileName = [NSString stringWithFormat:@"photo_%i.png", [dirContents count]];
    NSString *imagePath = [documentsDirectory stringByAppendingPathComponent:fileName];
    self.presentImageFileName = [NSString stringWithFormat:fileName];
    
    // Then save the image to the Documents directory.
    NSString *mediaType = [info objectForKey:UIImagePickerControllerMediaType];   
    if ([mediaType isEqualToString:@"public.image"]){
        UIImage *editedImage = [info objectForKey:UIImagePickerControllerOriginalImage];
        NSData *webData = UIImagePNGRepresentation(editedImage);
        [webData writeToFile:imagePath atomically:YES];
    }
}

#pragma mark - View lifecycle

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Being proactive as the ImagePicker takes a while to load.
    self.imagePicker = [[UIImagePickerController alloc] init];
}

- (void)viewDidUnload
{
    [super viewDidUnload];
    // Release any retained subviews of the main view.
    self.presentText = nil;
    self.presentImage = nil;
}

#pragma mark - Table view delegate

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    // The user can tap the cell (where the present image is) to change the photo.
    switch (indexPath.row) {
        case 0:
            [self presentPickerForPhoto];
            break;
        default:
            break;
    }
}

-(IBAction)cancel:(id)sender 
{
    [self dismissModalViewControllerAnimated:YES];
}

-(IBAction)done:(id)sender 
{
    
    // If the user just presses "Done" without setting an image, this would be blank.
    if (!self.presentImageFileName) {
        self.presentImageFileName = [NSString stringWithFormat:@"default_image.png"];
    }
    
    // Check to make sure we have a delegate and that it implements our method.
    if (self.delegate && [(id)self.delegate respondsToSelector:@selector(addChristmasItemToList:)]) {
        // Send back the newly created item
        NSDictionary *newPresent = [NSDictionary dictionaryWithObjectsAndKeys:self.presentText.text, @"text", self.presentImageFileName, @"imageName", nil];
        [self.delegate addChristmasItemToList:newPresent];
    }
    
    [self dismissModalViewControllerAnimated:YES];
}

@end

Charlie Sheen loves storyboards too!

Charlie Sheen loves storyboards too!

We’re going to benefit from Storyboards especially in this class, as you’ll notice that we don’t have to worry about creating the cells, number of cells, etc. And we don’t have to deal with the UITextView that we have in our static table. #winning!

What we are doing in this class, however, is catching when a user presses on the table cell that houses the present image. We cause this to bring up the photo picker, though we could use the camera as well.

When the user selects a picture, we save a copy to our Documents directory so we have it locally. Lastly, we update our delegate to reflect that we have a new present added.

Creating our second view controller will be easier than the first! Create a new file with the iOS\Cocoa Touch\UIViewController subclass template, enter ChristmasDetailsTableViewController for the class, make it a subclass of UITableViewController, and leave both checkboxes unchecked.

Open ChristmasDetailsTableViewController.h and replace it with the following:

#import 

@interface ChristmasDetailsTableViewController : UITableViewController
@property (nonatomic, strong) IBOutlet UITextView *presentText;
@property (nonatomic, strong) IBOutlet UIImageView *presentImage;
@property (nonatomic, strong) NSString *textHolder;
@property (nonatomic, strong) NSString *presentImageName;
- (IBAction)tweetButtonTapped:(id)sender;
@end

The purpose of this class is to show the details of the present and to provide the user with the option to tweet about it (really just an excuse to try out the new Twitter API :]).

Then switch to ChristmasDetailsTableViewController.m and replace it with the following:

#import "ChristmasDetailsTableViewController.h"
#import 

@implementation ChristmasDetailsTableViewController
@synthesize presentImage, presentText, textHolder, presentImageName;

#pragma mark - View lifecycle
- (void)viewDidLoad
{
    [super viewDidLoad];
}

- (void)viewDidUnload
{
    [super viewDidUnload];
    // Release any retained subviews of the main view.
    self.presentText = nil;
    self.presentImage = nil;
}

- (void)viewWillAppear:(BOOL)animated
{
    self.presentText.text = self.textHolder;
    
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];
    NSString *imagePath = [documentsDirectory stringByAppendingPathComponent:self.presentImageName];
    if ([[NSFileManager defaultManager] fileExistsAtPath:imagePath]) {
        UIImage *newImage = [UIImage imageWithContentsOfFile:imagePath];
        [self.presentImage setImage:newImage];  
    }
    
    [super viewWillAppear:animated];
}

- (IBAction)tweetButtonTapped:(id)sender { 
    if ([TWTweetComposeViewController canSendTweet])
    {
        TWTweetComposeViewController *tweetSheet = [[TWTweetComposeViewController alloc] init];
        [tweetSheet setInitialText:self.presentText.text];
        [tweetSheet addImage:self.presentImage.image];
        [self presentModalViewController:tweetSheet animated:YES];
    }
    else
    {
        UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Sorry" message:@"Tweeting is unavailable right now, check your internet connection and that you have at least one Twitter account set up" delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil];
        [alertView show];
    }
}
@end

As we’ll see in a minute, the details about the present are passed forward to us, so all we need to do is go find the image on the disk and display it, as we’ve already gotten the text from the sender.

The tweetButtonTapped: code is copied straight out of the great Twitter API tutorial on this! Gotta love “plug and play” code.

Step back to the MainStoryboard.storyboard file and change the “Class” property on the Add Table View Controller and the Detail Table VIew Controller scenes just like we did for the Table Cell. Set them to their respective new files and don’t forget to hook up the corresponding outlets.

Build and run again to ensure it still runs (although it still won’t display any presents you add).

Chris Lowe

Contributors

Chris Lowe

Author

Over 300 content creators. Join our team.