Objectively Speaking: A Crash Course in Objective-C

This is a post by iOS Tutorial Team Member Linda Burke, an indie iOS developer and the founder of canApps. Are you a software developer skilled in another platform, but want to start learning iPhone development (and hence Objective-C)? This was my situation not so long ago, and frankly, I’d gotten a bit rusty from […] By .

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.

Options, Options, Options

First you need to go back to your class header file, ViewController.h, and add a new property.

@property (nonatomic, retain) IBOutlet UISegmentedControl *quote_opt;

Here you’ve added a segmented control which allows you to select one item from a list of options – perfect for selecting a quote type.

And of course, you need to synthesize the new property. Add the following to the top of ViewController.m where the other @synthesize statements are:

@synthesize quote_opt;

Now go to ViewController.xib and drag a Segmented Control onto your view.

Change the properties of the control to the following:

  • Style: Bar (my personal preference)
  • Tint: whatever you like
  • Segments: 3
  • Select Segment: 0 and change the title to: Classic
  • Select Segment: 1 and change the title to: Modern
  • Select Segment: 2 and change the title to: Mine

This achieves the effect of having three different quote types – or rather, the ability to select between one of the three.

Having created your Segmented Control, you need to link it to the outlet in your class. You can use the same method as before to hook up the control to quote_opt in File’s Owner.

You will not need an action event for this control.

Before you do anything else, clear out the new property in ViewDidUnload.

quote_opt=nil;

Why don’t you build and run to see your new control on screen? It won’t do anything at the moment, but it’s nice to know it’s there!

The Joy of Predicates

A predicate is a useful object that filters an array. It’s a bit like having a select with a simple where clause in SQL. I find them quite useful when I have a categorized property list. It saves you from having to create separate property lists.

Don’t hate me, but you have to go back and change quote_btn_touch: in ViewController.m to use myQuotes instead of movieQuotes, as you will soon do something quite different for your movie quotes. And you need to put a condition around it, so that you’ll only use this when the third option is selected in the Segmented Control.

Or, if you prefer, simply replace quote_btn_touch: with the following code:

-(IBAction)quote_btn_touch:(id)sender {
    // 1 - Get personal quotes when the final segment is selected
    if (self.quote_opt.selectedSegmentIndex == 2) {
        // 1.1 - Get number of rows in array
        int array_tot = [self.myQuotes count];
        // 1.2 - Get random index
        int index = (arc4random() % array_tot);
        // 1.3 - Get the quote string for the index 
        NSString *my_quote = [self.myQuotes objectAtIndex:index];
        // 1.4 - Display the quote in the text view
        self.quote_text.text = [NSString stringWithFormat:@"Quote:\n\n%@",  my_quote];
    }
}

Now the user will see myQuotes only when they select the third option. As you’ll notice the rest of the code is the same as before, the only difference is that we display a quote (and that quote comes from the personal quote list) only when the segmented control has segment with index 2 selected. And as you might recall, since the segment control starts at index 0, index 2 means the third item.

Build and test your code to make sure that it works as you expect and that the quotes show up only when the “Mine” tab/segment is selected.

For the predicate fun, first you figure out the category you need based on the selected segment control and then use the category to create a filtered array of quotes that matches the category. Stay with me!

This is the code on the other side of the if statement in quote_btn_touch: – so simply add this to the end of the method to complete the “if” statement begun in section #1:

// 2 - Get movie quotes
else {
    // 2.1 - determine category
    NSString *selectedCategory = @"classic";
    if (self.quote_opt.selectedSegmentIndex == 1) {
        selectedCategory = @"modern";
    }
    // 2.2 - filter array by category using predicate
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"category == %@", selectedCategory];
    NSArray *filteredArray = [self.movieQuotes filteredArrayUsingPredicate:predicate];
    // 2.3 - get total number in filtered array
    int array_tot = [filteredArray count];
    // 2.4 - as a safeguard only get quote when the array has rows in it
    if (array_tot > 0) {
        // 2.5 - get random index
        int index = (arc4random() % array_tot);
        // 2.6 - get the quote string for the index 
        NSString *quote = [[filteredArray objectAtIndex:index] valueForKey:@"quote"];
        self.quote_text.text = [NSString stringWithFormat:@"Movie Quote:\n\n%@",  quote];
    } else {
        self.quote_text.text = [NSString stringWithFormat:@"No quotes to display."];
    }
}

Okay, build and run. Check that you see the right type of quote depending on your selection. If you are always getting the same type, my guess would be that you may not have linked the Segmented Control to your class.

The String Symphony

So far, so good! Now let’s explore some different string options and syntax in Objective-C.

If the quote has a source in the property list, then the app should display that as well. To check if there’s a value in a string, you can check the length of the string.

So add the following to quote_btn_touch: after the first line in section #2.6 (the first line not counting the comment, that is):

    // 2.7 - Check if there is a source    
    NSString *source = [[filteredArray objectAtIndex:index] valueForKey:@"source"];
    if (![source length] == 0) {
        quote = [NSString stringWithFormat:@"%@\n\n(%@)",  quote, source];
    }
    // 2.8 - Set display string

You get the source from the array and check that it contains a value by making sure that its length is not zero. ! represents NOT. Use == when checking if an integer is equal to a value.

Then you build a new display string by combining the quote and the source using stringWithFormat.

To make things more interesting, why don’t you display something slightly different for quotes from classic movies that will involve checking the value of the category of the selected quote?

Replace section #2.8 in quote_btn_touch: with the following:

// 2.8 - Customize quote based on category
if ([selectedCategory isEqualToString:@"classic"]) {
    quote = [NSString stringWithFormat:@"From Classic Movie\n\n%@",  quote];
} else {
    quote = [NSString stringWithFormat:@"Movie Quote:\n\n%@",  quote];
}
// 2.9 - Display quote
self.quote_text.text = quote;

This checks if the string is equal to a specific value, in this case “classic”, and customizes the label for the quote based on the category.

If you want to check for a particular movie title (or for that matter any other string attribute) starts with a particular value, you can do that too. Say you want to display some extra text if the quote is from a Harry Potter movie – add the following right above section #2.9:

if ([source hasPrefix:@"Harry"]) {
    quote = [NSString stringWithFormat:@"HARRY ROCKS!!\n\n%@",  quote];
}

As you can guess, hasPrefix is used to check if the start of the string has a particular text value.

Build and run your app to make sure that it works as you expect it to. Pay attention to the different categories and to Harry Potter movie quotes to make sure that it all works correctly.