How To Use Blocks in iOS 5 Tutorial – Part 2

This is a blog post by iOS Tutorial Team member Adam Burkepile, a full-time Software Consultant and independent iOS developer. Check out his latest app Pocket No Agenda, or follow him on Twitter. Welcome back to our tutorial series on using blocks in iOS 5 – with some Storyboard/Interface Builder practice along the way! In […] By .

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

UIAnimation

Let's go back and add a little visual flair with another block method. Replace the ibaRemoveItem: and ibaAddItemMethod: code with the following:

- (IBAction)ibaRemoveItem:(id)sender {
    IODItem* currentItem = [self.inventory objectAtIndex:currentItemIndex];
    [order removeItemFromOrder:currentItem];
    [self updateOrderBoard];
    [self updateCurrentInventoryItem];
    [self updateInventoryButtons];
	
    UILabel* removeItemDisplay = [[UILabel alloc] initWithFrame:ibCurrentItemImageView.frame];
    [removeItemDisplay setCenter:ibChalkboardLabel.center];
    [removeItemDisplay setText:@"-1"];
    [removeItemDisplay setTextAlignment:UITextAlignmentCenter];
    [removeItemDisplay setTextColor:[UIColor redColor]];
    [removeItemDisplay setBackgroundColor:[UIColor clearColor]];
    [removeItemDisplay setFont:[UIFont boldSystemFontOfSize:32.0]];
    [[self view] addSubview:removeItemDisplay];
	
    [UIView animateWithDuration:1.0
                     animations:^{
                         [removeItemDisplay setCenter:[ibCurrentItemImageView center]];
                         [removeItemDisplay setAlpha:0.0];
                     } completion:^(BOOL finished) {
                         [removeItemDisplay removeFromSuperview];
                     }];
	
}

- (IBAction)ibaAddItem:(id)sender {
    IODItem* currentItem = [self.inventory objectAtIndex:currentItemIndex];
    [order addItemToOrder:currentItem];
    [self updateOrderBoard];
    [self updateCurrentInventoryItem];
    [self updateInventoryButtons];
	
    UILabel* addItemDisplay = [[UILabel alloc] initWithFrame:ibCurrentItemImageView.frame];
    [addItemDisplay setText:@"+1"];
    [addItemDisplay setTextColor:[UIColor whiteColor]];
    [addItemDisplay setBackgroundColor:[UIColor clearColor]];
    [addItemDisplay setTextAlignment:UITextAlignmentCenter];
    [addItemDisplay setFont:[UIFont boldSystemFontOfSize:32.0]];
    [[self view] addSubview:addItemDisplay];
	
    [UIView animateWithDuration:1.0
                     animations:^{
                         [addItemDisplay setCenter:ibChalkboardLabel.center];
                         [addItemDisplay setAlpha:0.0];
                     } completion:^(BOOL finished) {
                         [addItemDisplay removeFromSuperview];
                     }];
}

The above might seem like a lot of code but it's really quite simple. The first new code segment we added just creates a UILabel and sets its properties. The second segment is an animation that moves the label that we just created. This is an example of the Block UIView animation system that we outlined at the beginning of this tutorial.

Compile and run and you'll see a nifty animation which shows the items being added and removed each time you tap the "+1" or "-1" buttons.

Getting the Total

The last helper method we are going to add to IODOrder.m is the method to total the order and return the result.

- (float)totalOrder {
	// 1 - Define and initialize the total variable
    __block float total = 0.0;
	// 2 - Block for calculating total
    float (^itemTotal)(float,int) = ^float(float price, int quantity) {
        return price * quantity;
    };
	// 3 - Enumerate order items to get total
    [self.orderItems enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
        IODItem* item = (IODItem*)key;
        NSNumber* quantity = (NSNumber*)obj;
        int intQuantity = [quantity intValue];
        total += itemTotal(item.price, intQuantity);
    }];
	// 4 - Return total
    return total;
}

Let's go through the above code step-by-step:

  1. We define and initialize the variable that will accumulate the total. Note the __block keyword. We will be using this variable inside of a Block. If we do not use the __block keyword, the Block we create below would create a const copy of this variable and use that when referenced inside the Block, meaning we would not be able to change the value inside of the Block. By adding this keyword we are able to be read from AND write to the variable inside of the Block.
  2. Then, we define a Block variable and assign it a Block that simply takes a price and a quantity and returns the item total based on price and quantity.
  3. This code segment goes over every object in the orderItems dictionary using a Block method, enumerateKeysAndObjectsUsingBlock: and uses the previous Block variable to find the total for each item for the quantity ordered and then adds that to the grand total (which is why we needed the __block keyword on the total variable since it is being modified inside of a Block).
  4. Once we are done calculating the total for all the items, we simply return the calculated total.

Go back to IODOrder.h and add the prototypes:

- (float)totalOrder;

The last thing to do is to add the total calculation functionality to the app. All the heavy work will be done by the totalOrder method and so, all we have to do is show the calculated total to the user when they hit the total button and trigger the ibaCalculateTotal: action. So fill in the ibaCalculateTotal: stub in IODViewController.m with the following:

- (IBAction)ibaCalculateTotal:(id)sender {
    float total = [order totalOrder];
    UIAlertView* totalAlert = [[UIAlertView alloc] initWithTitle:@"Total" 
                                                         message:[NSString stringWithFormat:@"$%0.2f",total] 
                                                        delegate:nil
                                               cancelButtonTitle:@"Close" 
                                               otherButtonTitles:nil];
    [totalAlert show];
}

This just gets the total, creates a simple alert view, and shows it to the user.

That's it! Give it a final build and run, and maybe even grab a burger to celebrate! :]

Useful Blocks Cheat Sheet

Before you go, I wanted to let you know about a few block methods that you might find useful.

NSArray

  • enumerateObjectsUsingBlock - Probably the Block method I use the most, it basically is a simpler, cleaner foreach.
  • enumerateObjectsAtIndexes:usingBlock: - Same as enumerateObjectsUsingBlock: except you can enumerate a specific range of items in the array instead of all the items. The range of items to enumerate is passed via the indexSet parameter.
  • indexesOfObjectsPassingTest: - The Block returns an indexset of the the objects that pass a test specified by the Block. Useful for looking for a particular group of objects.

NSDictionary

UIView

Grand Central Dispatch

Creating Your Own Blocks

Also, sometimes you might want to create your own methods that take blocks. Here's some code snippets showing you how you can do that:

// Here's a method that takes a block
- (void)doMathWithBlock:(int (^)(int, int))mathBlock {
    self.label.text = [NSString stringWithFormat:@"%d", mathBlock(3, 5)];
}

// Calling that method with a block
- (IBAction)buttonTapped:(id)sender {
    [self doMathWithBlock:^(int a, int b) {
        return a + b;
    }];
}

Since a block is just an Objective-C object, you can store it in a property so you can call it later. This is useful if you want to call the method after some asynchronous task has completed, such as a network task. Here's an example:

// Declare property
@property (strong) int (^mathBlock)(int, int); // Use copy if not using ARC

// Synthesize property
@synthesize mathBlock = _mathBlock;

// Store block so you can call it later
- (void)doMathWithBlock:(int (^)(int, int))mathBlock {
    self.mathBlock = mathBlock;
}

// Calling that method with a block
- (IBAction)buttonTapped:(id)sender {
    [self doMathWithBlock:^(int a, int b) {
        return a + b;
    }];
}

// Later on...
- (IBAction)button2Tapped:(id)sender {
    self.label.text = [NSString stringWithFormat:@"%d", self.mathBlock(3, 5)];
}

Finally, you can simplify your syntax a bit by using typedefs. Here's the previous example cleaned up a bit with a typedef for the block:

// Create typedef for block
typedef int (^MathBlock)(int, int);

// Create property using typedef
@property (strong) MathBlock mathBlock;

// Synthesize property
@synthesize mathBlock = _mathBlock;

// Method that stores block for use later
- (void)doMathWithBlock:(MathBlock) mathBlock {
    self.mathBlock = mathBlock;
}

// Calling that method with a block
- (IBAction)buttonTapped:(id)sender {
    [self doMathWithBlock:^(int a, int b) {
        return a + b;
    }];
}

// Later on...
- (IBAction)button2Tapped:(id)sender {
    self.label.text = [NSString stringWithFormat:@"%d", self.mathBlock(3, 5)];
}