iOS Tutorial: How To Create A Simple iPhone App Tutorial: Part 2/3

An iOS tutorial for complete beginners that shows you how to make your first iPhone app, from scratch! By Ray Wenderlich.

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

Implementing Your Detail View

You’re going to make a bunch of changes to RWTDetailViewController.m. There’s a lot of code here, so you better go over it part by part.

1) Import headers

// At top of file
#import "RWTScaryBugDoc.h"
#import "RWTScaryBugData.h"
#import "RWTUIImageExtras.h"

// Add in synthesize section
@synthesize picker = _picker;

You should be a pro at this by this point!

2) Set up Rate View

// Replace configureView with the following
- (void)configureView
{
    // Update the user interface for the detail item.
    self.rateView.notSelectedImage = [UIImage imageNamed:@"shockedface2_empty.png"];
    self.rateView.halfSelectedImage = [UIImage imageNamed:@"shockedface2_half.png"];
    self.rateView.fullSelectedImage = [UIImage imageNamed:@"shockedface2_full.png"];
    self.rateView.editable = YES;
    self.rateView.maxRating = 5;
    self.rateView.delegate = self; 
}

In configureView (which is called from viewDidLoad), you set up the properties of your RWTRateView. For more details, check out the How To Make a Custom UIView in iOS 5: A 5-Star Rating View tutorial.

3) Enable autorotation

// Implement the method shouldAutorotateToInterfaceOrientation
- (BOOL)shouldAutorotateToInterfaceOrientation {
    return YES;
}

In shouldAutorotateToInterfaceOrientation, you return YES since you went to do all the work of setting up the autosizing attributes in Interface Builder! This will allow the user to rotate this view between orientations, and your controls will re-layout according to the autosizing attributes you set up.

4) Set up initial UI state

// Add to the end of configureView
if (self.detailItem) {
    self.titleField.text = self.detailItem.data.title;
    self.rateView.rating = self.detailItem.data.rating;    
    self.imageView.image = self.detailItem.fullImage;
}

Here you simply set up your GUI based on the bug that was selected.

5) Handle text view and rating view

- (IBAction)titleFieldTextChanged:(id)sender {
    self.detailItem.data.title = self.titleField.text;
}

#pragma mark UITextFieldDelegate

- (BOOL)textFieldShouldReturn:(UITextField *)textField {
    [textField resignFirstResponder];
    return YES;
}

#pragma mark RWTRateViewDelegate

- (void)rateView:(RWTRateView *)rateView ratingDidChange:(float)rating {
    self.detailItem.data.rating = rating;
}

You set up titleFieldValueChanged to be called whenever the user changes the value of the text field, so you update the model as well whenever it changes.

textFieldShouldReturn is called when the user hits the return key on the keyboard. You call resignFirstResponder to get the keyboard to disappear off the screen when that happens.

rateView:ratingIsChanged is called when the user chooses a new rating since you set yourself as the RWTRateView‘s delegate, so when that happens you update your model.

In case you were wondering, the #pragma marks are just special lines that XCode can read to set up separators in the editor’s function list for organization’s sake:

Using pragma mark to organize code in Xcode

5) Display image picker and process results

- (IBAction)addPictureTapped:(id)sender {
    if (self.picker == nil) {   
        self.picker = [[UIImagePickerController alloc] init];
        self.picker.delegate = self;
        self.picker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
        self.picker.allowsEditing = NO;    
    } 
    [self presentViewController:_picker animated:YES completion:nil];
}

#pragma mark UIImagePickerControllerDelegate

- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker {
   [self dismissViewControllerAnimated:YES completion:nil];
}

- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {    
    
   [self dismissViewControllerAnimated:YES completion:nil];
    
    UIImage *fullImage = (UIImage *) [info objectForKey:UIImagePickerControllerOriginalImage]; 
    UIImage *thumbImage = [fullImage imageByScalingAndCroppingForSize:CGSizeMake(44, 44)];
    self.detailItem.fullImage = fullImage;
    self.detailItem.thumbImage = thumbImage;
    self.imageView.image = fullImage;
}

You set up addPictureTapped to be called whenever the user taps the invisible button above the UIImage, so here you create the UIImagePicker (if it doesn’t exist already), and set the photo source to photo library (you can choose other things such as camera as well). You set yourself as the delegate so you can get callbacks when the user finished picking the picture. Finally, you present the image picker as a modal view controller, which means it takes up the whole screen.

Finally, you implement the image picker callbacks for when the user picks an image or cancels. Either way, you dismiss the modal view controller. If the user did pick the image, you get the full image and also a thumbnail version (which you resize with the RWTUIImageExtras class that you added earlier), and update both the model and the view.

OMG – you’re probably sick of writing code now eh? Don’t worry – you’re almost done, just gotta hook this baby in!

Integrating Your Detail View

This should be pretty quick. First, open Main.storyboard, and select the table view cell in the Master View Controller. Control-drag from that cell over to the Detail View Controller, and a popup will appear asking if you want to connect them as a Push, Modal, or Custom. Select Push, and you should see an arrow connecting the view controllers:

Connecting the view controllers with a push segue

Now you just need to make it pass the correct bug onto the detail view controller when a row is selected. To do this, open RWTMasterViewController.m and make the following changes:

// Implement the method didMoveToParentViewController
-(void)didMoveToParentViewController:(UIViewController *)parent{
    [self.tableView reloadData];
}

// Modify the prepareForSegue method by
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    RWTDetailViewController *detailController =segue.destinationViewController;
    RWTScaryBugDoc *bug = [self.bugs objectAtIndex:self.tableView.indexPathForSelectedRow.row];
    detailController.detailItem = bug;
}

First, note that in didMoveToParentViewController, you reload the data in the table. This is because when the user is in the detail view, they might change the name of the bug or the picture, and you want the updated name/picture to show up when they come back to the table view. One easy way to do that is to reload the entire table, so you do that here.

Next, remember that you set things up in the Storyboard Editor that whenever a row is tapped, it will push the Detail View Controller onto the stack. When this happens, the prepareForSegue will be called, so you have a chance to give the detail view controller any information it needs. In this case, you simply pass the selected bug on to display.

Finally, you’re done! Go ahead and compile and run your project, and if all goes well you should now be able to bring up the bug detail view, change the names of the bugs, change their pictures, rate them, and even rotate to any orientation!

Detail View Controller Example

Where To Go From Here?

Here is a sample project with all of the code you’ve developed so far in this iOS tutorial series.

Please let me know if anything in the above is confusing or if you’d like me to go into more detail about anything.

In the final part of the series, you’ll learn how to add and delete bugs, add an icon and default image to your project, and correctly handle long-running operations!