Objectively Speaking 2: A Crash Course in Objective-C for iOS 6

This is the 2nd post by iOS Tutorial Team Member Linda Burke, an indie iOS developer and the founder of canApps. If you are a software developer skilled in another platform, but want to start learning iPhone development (and hence Objective-C) – this is the tutorial series for you! This tutorial picks up where the […] By .

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

Providing Feedback Through the UI

It’s always a great idea to provide some feedback to the user as they navigate through your app.
When an answer button is touched you’ll change the background color of the answer label. Green is for correct answers. Red is for incorrect answers. A new question reverts the labels to the original color.

To modify the various UI elements in your app from code, you’ll first need to define some private properties so that you can access them later.

Add the following code to QuoteQuizViewController.m, just after the @interface statement:

@property (weak, nonatomic) IBOutlet UILabel * questionLabel;
@property (weak, nonatomic) IBOutlet UILabel * answer1Label;
@property (weak, nonatomic) IBOutlet UILabel * answer2Label;
@property (weak, nonatomic) IBOutlet UILabel * answer3Label;
@property (weak, nonatomic) IBOutlet UIButton * answer1Button;
@property (weak, nonatomic) IBOutlet UIButton * answer2Button;
@property (weak, nonatomic) IBOutlet UIButton * answer3Button;
@property (weak, nonatomic) IBOutlet UIImageView * movie1;
@property (weak, nonatomic) IBOutlet UIImageView * movie2;
@property (weak, nonatomic) IBOutlet UIImageView * movie3;
@property (weak, nonatomic) IBOutlet UILabel * statusLabel;
@property (weak, nonatomic) IBOutlet UIButton * startButton;
@property (weak, nonatomic) IBOutlet UIButton * infoButton;

While these properties are privately scoped, Interface Builder (the actual user interface editor) will know where to look in your class to find them. Remember that the IBOutlet keyword means that this is an object that can be linked to an interface element.

Also, you’ll need a private property to keep track of the current answer.

Add the following line to the end of the list of @property declarations in QuoteQuizViewController.m:

@property (assign, nonatomic) NSInteger answer;

You’ll need a way to handle the end-game scenario, when the player has answered all of the questions.

Add the following code to QuoteQuizViewController.m just before the final @end :

- (void)quizDone
{
    // More later
}

Hey, there’s no code in this method!

Yep — it’s just stubbed out for now to satisfy the compiler. You’ll flesh out this method later on in the tutorial, but for now you can leave it as-is.

Once the player has answered the first question, you’ll want to move on to the next question and present all the questions in sequence. To do so, you’ll need a method to get the next quote in line.

Add the following code to QuoteQuizViewController.m, just before the final @end:

- (void)nextQuizItem
{
     if (self.quizIndex == 999) {
        self.quizIndex = 0;
        self.statusLabel.text = @"";
    } else if ((self.quiz.quizCount-1) > self.quizIndex) {
        self.quizIndex++;
    } else {
        self.quizIndex = 0;
        self.statusLabel.text = @"";
    }
    
    if (self.quiz.quizCount >= self.quizIndex + 1) {
        [self.quiz nextQuestion:self.quizIndex];
        self.questionLabel.text = self.quiz.quote;
        self.answer1Label.text = self.quiz.ans1;
        self.answer2Label.text = self.quiz.ans2;
        self.answer3Label.text = self.quiz.ans3;
    } else {
        self.quizIndex = 0;
        [self quizDone];
    }
    
    // reset fields for next question
    self.answer1Label.backgroundColor = [UIColor colorWithRed:51/255.0 green:133/255.0 blue:238/255.0 alpha:1.0];
    self.answer2Label.backgroundColor = [UIColor colorWithRed:51/255.0 green:133/255.0 blue:238/255.0 alpha:1.0];
    self.answer3Label.backgroundColor = [UIColor colorWithRed:51/255.0 green:133/255.0 blue:238/255.0 alpha:1.0];

    self.answer1Button.hidden = NO;
    self.answer2Button.hidden = NO;
    self.answer3Button.hidden = NO;
}

You now need to make a call to nextQuizItem to start the game once your app has finished initializing.

Add the following code to the bottom of viewDidLoad in QuoteQuizViewController.m:

[self nextQuizItem];

Now you must link up each of the scene objects so that you can access them in your code.

Click on MainStoryboard.storyboard to display the Quite Quiz View Controller Scene if it’s not already displayed.

Bring up the connection dialog by right clicking on the scene name — Quote Quiz View Controller.

In the connection dialog, find the questionLabel outlet. Click the circle on the right hand side of the outlet and drag it to the question label on the screen, like so:

Setting up the outlets

Now repeat the process to hook up the following outlets to their respective controls: answer1Label, answer2Label, answer3Label, answer1Button, answer2Button, and finally answer3Button.

Your outlets dialog should look like the following:

Outlets dialog

Build and run, and your app should look like the screenshot below:

Running the app

Hm, it would be nice for the background color of the question to match that of the answers. You could do this in the Attributes Inspector, but you can also do this in code.

Open QuoteQuizViewController.m and add the following line at the bottom of viewDidLoad:

    self.questionLabel.backgroundColor = [UIColor colorWithRed:51/255.0 green:133/255.0 blue:238/255.0 alpha:1.0];

Build and run, and yep – they seem to match now:

Running for the second time

If you receive any errors, don’t panic. Compare your code with the tutorial’s code, and pay close attention to the case.

If your app does compile and run, you should see your first quote!

Okay, you’ve hit the on-ramp in your Objective-C road test, and now it’s time to merge on to the interstate! :]

Adding Actions to Your Buttons

Open QuoteQuizViewController.h add the following lines after the @interface declaration:

- (IBAction)ans1Action:(id)sender;
- (IBAction)ans2Action:(id)sender;
- (IBAction)ans3Action:(id)sender;
- (IBAction)startAgain:(id)sender;

The lines above create the action methods for the answer buttons, as well as a method to restart the quiz. The IBAction keyword allows the method to be linked to a view object.

Now you need to add the implementation of these four new methods.

Add the following code above the final @end declaration in QuoteQuizViewController.m:

- (void)checkAnswer
{
    if ([self.quiz checkQuestion:self.quizIndex forAnswer:_answer]) {
        if (self.answer == 1) {
            self.answer1Label.backgroundColor = [UIColor greenColor];
        } else if (self.answer == 2) {
            self.answer2Label.backgroundColor = [UIColor greenColor];
        } else if (self.answer == 3) {
            self.answer3Label.backgroundColor = [UIColor greenColor];
        }
    } else {
        if (self.answer == 1) {
            self.answer1Label.backgroundColor = [UIColor redColor];
        } else if (self.answer == 2) {
            self.answer2Label.backgroundColor = [UIColor redColor];
        } else if (self.answer == 3) {
            self.answer3Label.backgroundColor = [UIColor redColor];
        }
    }
    
    self.statusLabel.text = [NSString stringWithFormat:@"Correct: %d Incorrect: %d",self.quiz.correctCount, self.quiz.incorrectCount];
    
    self.answer1Button.hidden = YES;
    self.answer2Button.hidden = YES;
    self.answer3Button.hidden = YES;
    
    self.startButton.hidden = NO;
    
    [self.startButton setTitle:@"Next" forState:UIControlStateNormal];
}

- (IBAction)ans1Action:(id)sender 
{
    self.answer = 1;
    [self checkAnswer];
}

- (IBAction)ans2Action:(id)sender 
{
    self.answer = 2;
    [self checkAnswer];
}

- (IBAction)ans3Action:(id)sender 
{
    self.answer = 3;
    [self checkAnswer];    
}

- (IBAction)startAgain:(id)sender 
{    
    [self nextQuizItem];
}

The ans1Action and sibling methods set the user’s answer, then call checkAnswer to determine if the answer was correct.

checkAnswer changes the color of the corresponding label to indicate if the answer is correct or not. The user interface then updates to display the label change.

Now, you need to connect the above action methods in your code to the buttons on your interface.

Open MainStoryboard.storyboard. Select the first button, then right-click and drag to the scene name Quote Quiz View Controller, as shown below:

Connecting the IBAction to the button

A list of IBActions will appear next to the button. Select ans1Action to connect the action with the button, like so:

Select the first action

The action method will be associated with the default event of “touch-up inside”. The “touch-up inside” event initiates the action method once the user has lifted their finger off the button. This allows a user to cancel the button press by sliding their finger off the button.

Link up the rest of the buttons with their designated action method.

Then build and run your app, and test out the controls by selecting an answer from the ones presented. An incorrect answer will turn the label red, while a correct answer will turn the label green, as seen in the screenshot below:

Selecting an answer

Looks good so far, but there’s one little problem: there’s no way to move on to the next question! As well, there’s nothing to indicate the current score to the player.

To fix this, open MainStoryboard.storyboard. First, you’ll need to make some room for the player’s score.

Hold down the command button, and select all of the answer labels and buttons. Drag them as a group to leave some space between the Question label and the first answer button and label — about thirty points total.

Next, drag a new label into the space you just created and extend its width to match the width of the Question label. Change the label’s text to “Correct: Incorrect:”, as shown in the screenshot below:

Adding a new label

Next, add a button to the interface, and position it on the right hand side of the view, flush with the last answer label. Set the width of the button to be 119 points, and the height to be 44 points. Finally, change the text of the button to read “Next”, as seen here:

Adding the next button

Hook up the outlets by right-clicking the scene name Quote Quiz View Controller and drag to the “Correct: Incorrect” label. Select statusLabel from the available outlets. Do the same for the button, selecting startButton outlet.

Now, right click and drag from the button to the scene name, and select the startAgain action.

Finally, flesh out quizDone in QuoteQuizViewController.m as follows:

- (void)quizDone
{
    if (self.quiz.correctCount) {
        self.statusLabel.text = [NSString stringWithFormat:@"Quiz Done - Score %d%%", self.quiz.quizCount / self.quiz.correctCount];    
    } else {
        self.statusLabel.text = @"Quiz Done - Score: 0%";
    }
    [self.startButton setTitle:@"Try Again" forState:UIControlStateNormal];
    self.quizIndex = 999;
}

quizDone first checks if the user has correctly answered any questions. If so, it does a simple calculation and displays the number of correct answers. Otherwise, it outputs a pre-defined message, preventing any possible division by zero errors. The method then resets the button to try again, and finally resets the quiz index.

Build and run your app, and now you should now have a fully functional quiz!

Advancing to the next question

Feel free to play around with the quiz — and see how much of a movie buff you are by seeing how many answers you can get right.

With the quiz done, it’s time to put the pedal to the metal and see how fast you can really drive! :]

The user may appreciate a little help every so often, so your app should probably provide some clues to assist the user.