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.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Basic Security in iOS 5 – Part 2
40 mins
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.
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).
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
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).