Blog Archives

Do Just About Anything With Blocks

I love using blocks in objective-C (and C for that matter).  I think they’re the most useful additions to language since . . . well I don’t know.  I allows your code to be more compact and more logically grouped (remember UIView animation delegates? yeesh).  In some cases, even faster by backgrounding certain operations with GCD.   Sadly, blocks have only made it into a small portion on the SDKs and frameworks, most notably collection enumeration and the vastly improved UIView animations.  Fortunately there’s some enterprising individuals and groups who’ve created some very good libraries for extending blocks to other parts of the framework.

BlocksKit

BlocksKit is an open source library that allows you to do just about anything with blocks.  You can easily apply blocks as selectors to UIControls, UIGestureRecognizers, and NSTimers.  There are facilities to apply blocks in different ways to collections, and through the use of dynamic delegates you can use blocks in place of delegates on alerts and action sheets.  As a bonus there’s some handy utilities for dealing with associated objects too.

Block based drawing

I’ve shared this before,but David Hamrick posted a great demo on using blocks to handle drawing on UIViews without the need to subclass.  Find it here.

UIAlertView+Blocks:

If you don’t need everything in BlocksKit, you can check out UIAlertView+Blocks which works very nicely for working with alerts and actions sheets without the need for a delegate at all.

Note: BlocksKit and the above categories don’t play nice together.  You’ll need to pick on or the other to avoid some very odd runtime bugs.

Some block tips of my own:

Static comparators

Apple added block based comparisons for sorting NSArrays.  The block takes 2 objects and returns an NSComparisonResult.  If you find yourself sorting the same thing in multiple places it’s often faster to statically declare your comparator that you can reuse.  Let’s say we have an array of a custom class person, with strings for first and last name:


@interface Person : NSObject
@property (nonatomic, copy) NSString *firstName, *lastName;
@end

Sorting using a block is simple enough, given an array of Person objects:


[people sortUsingComparator:^NSComparisonResult(id p1, id p2) {
if ([[p1 lastName] isEqualToString:[p2 lastName]])
{
return [[p1 firstName] caseInsensitiveCompare:[p2 firstName]];
}
return [[p1 lastName] caseInsensitiveCompare:[p2 lastName]];
}];

If we want to reuse the comparator multiple places, we store it into a static variable:


static NSComparator personCompare = ^NSComparisonResult(id p1, id p2) {
if ([[p1 lastName] isEqualToString:[p2 lastName]])
{
return [[p1 firstName] caseInsensitiveCompare:[p2 firstName]];
}
return [[p1 lastName] caseInsensitiveCompare:[p2 lastName]];
};

// ...

[people sortUsingComparator:personCompare];

You can do the same thing with literally any block you want to reuse, but you don’t get the benefit capturing scoped variables.

Blocks in forms:

It’s common to have forms made of table cells with text fields for entering user data.  Normally to properly apply the captured data you either have to do some creative work with UIView tags or extend the UITableViewDelegate to hand your new cell type.  You can also use blocks to handle the callback by either adding a block variable to a custom table cell.


typedef void(^TextViewEndCallback)(NSString *enteredText);

@interface MultiLineTextFieldCell : UITableViewCell <UITextViewDelegate>
{
UITextView *textView;
SBTextViewEndCallback textViewEndCallback;
}
@property (nonatomic, retain) IBOutlet UITextView *textView;
- (void)setTextViewEndCallback:(TextViewEndCallback)callback;
@end

The above example uses a text view in a table cell.  The table cell should be delegate for the text view.  In the text view delegate method you need only call the block passed into the cell:


- (void)textViewDidEndEditing:(UITextView *)textView_
{
textViewEndCallback(textView.text);
}

This is very easy to use in your cellForRowAtIndexPath method:


__block SomeClass *object = [objects objectAtIndex:indexPath.row];

[cell setTextViewEndCallback:^(NSString *enteredText) {
object.somestring = enteredText;
}];

We used very similar ideas to the above in SalesBag to speed development along.

That’s it!

I enjoy how blocks can speed development along and allows you to write more flexible and sometimes even more reusable code.  Have any cool block tricks?  I’d love to hear them.  Happy Coding!

More CADisplayLink

Hello to everyone coming from iDevBlogADay!

Since this is my first post to be pulled into the site, I’ll take just a quick moment to introduce myself.  My name is Pat Zearfoss and I’m one of the lead iOS developers at Mindgrub Technologies, a development shop in Baltimore, MD, USA.  We’re primarily a work-for-hire shop, but we do have a couple products we’ve released on our own.  I’ve been working on iOS since the SDK became public and because of the nature of my work at Mindgrub, I’ve had the opportunity to work on a variety of projects, large and small.

This site is a collection of thoughts on iOS development, usually spurned from a project I’m working on.  Lately I’ve been spending a lot of time in CoreAnimation, so as a result you’ll find a lot on that topic.  I usually try to jot down things that weren’t immediately obvious to me as I work through a project, usually figuring that if I had to really dig to find a solution, someone else probably will to.

I was rattling my brain to try and come up with a good introductory topic for my first iDevBlogADay post and I eventually settled upon doing another post about CADisplayLink.  Not that this site gets a ton of traffic, but CADisplayLink tops my search terms daily.  Documentation around the web on it is scarce as it’s a pretty niche topic, although highly useful when the animation you want to create in an iOS app is something not easily handled by transitions or stock animations.

So what is a CADisplayLink.  Directly from Apple:

A CADisplayLink object is a timer object that allows your application to synchronize its drawing to the refresh rate of the display.

CADisplayLink works very much like an NSTimer.  You can set it to call a method on every tick, but instead of setting a time interval, the time interval is determined by the screen’s refresh rate.  When you’re ready to use the display link you add it to the run loop, and the method you handed to it will fire until you call the “invalidate” method on it.  CADisplayLink lives in the QuartzCore framework.

In an earlier post, I showed how to draw a simple line on a CALayer using CADisplayLink, drawing an additional pixel every time.  Today I’m going to show you how to draw a circle fanning out (similar to how the pie charts render in Mint.com) and setting a desired time duration for that animation. To get started, we’re going to create a new project with a single view controller and UIView subclass.  Here’s my project layout:

We’ll also need to add the QuartzCore framework to the project.  (Target settings -> Build Phases):

Next in viewDidLoad of our view controller, we need to create a new instance of our UIView subclass and add it to the view:


- (void)viewDidLoad
{
    [super viewDidLoad];
    CircleDisplayView *circleView =
        [[CircleDisplayView alloc] initWithFrame:self.view.bounds];
    [self.view addSubview:circleView];
    [circleView release];
}

Now onto our custom view. This is where we’ll be using the CADisplayLink. Here’s my .h file:

#import <QuartzCore/QuartzCore.h>

@interface CircleDisplayView : UIView
{
    CADisplayLink *displayLink;

    BOOL animationRunning;
    NSTimeInterval drawDuration;
    CFTimeInterval lastDrawTime;
    CGFloat drawProgress;
}
@end

Our view only needs to keep track of a couple things. First we need the CADisplayLink. We’ll need to keep track of it during drawing.  We’ll also need a flag to know whether our animation is running (animationRunning) and some variables to keep track of timestamps(lastDrawTime), the drawing progress (drawProgress), as well as the amount of time we want the animation to run for (drawDuration)

In the initializer, all we’ll need to do is get an instance of CADisplayLink and set the amount of time we want for the animation:

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self)
    {
        displayLink = [CADisplayLink displayLinkWithTarget:self
                                                  selector:@selector(setNeedsDisplay)];
        drawDuration = 3.0;
    }
    return self;
}

The method we’ll be using in the CADisplayLink is simply setNeedsDisplay.  So, while the animation is running, on every tick of the display link we’ll be asking the view to redraw itself.  All the magic then happens in drawRect:

- (void)drawRect:(CGRect)rect
{
    if (!animationRunning)
    {
        [displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
        animationRunning = YES;
        return;
    }

    if (lastDrawTime == 0)
    {
        lastDrawTime = displayLink.timestamp;
        return;
    }

    CGContextRef ctx = UIGraphicsGetCurrentContext();
    CGContextSetFillColorWithColor(ctx, [[UIColor blueColor] CGColor]);

    CFTimeInterval elapsedTime = displayLink.timestamp - lastDrawTime;
    NSLog(@"elapsed %f", elapsedTime);

    CGFloat radiansToDraw = drawProgress + ((2 * M_PI) / drawDuration) * elapsedTime;

    NSLog(@"drawing %f radians", radiansToDraw);

    CGContextMoveToPoint(ctx, self.center.x, self.center.y);
    CGContextAddLineToPoint(ctx, self.center.x + 100, self.center.y);
    CGContextAddArc(ctx, self.center.x, self.center.y, 100, 0, radiansToDraw, 0);
    CGContextClosePath(ctx);
    CGContextFillPath(ctx);

    lastDrawTime = displayLink.timestamp;
    drawProgress = radiansToDraw;

    if (radiansToDraw > 2 * M_PI)
    {
        NSLog(@"Invalidate display link");
        [displayLink invalidate];
        animationRunning = NO;
        lastDrawTime = 0;
    }

}

Most of this code is standard drawing code: adding lines and arcs, so I’m not going to go into great detail on those, but rather focus on the bits having to do with making the circle drawing occur progressively.

The first thing to notice is that on the first (and second) pass through drawRect, no actual drawing occurs.  The first pass occurs when the view naturally tries to render itself.  On this pass all we need to do is add the displayLink to the run loop and return.

Second pass we get caught in the second if statement, ensuring that we seed lastDrawTime.  We need only know about the differences in two display link timestamps, so this is necessary.

Finally on the third pass we begin drawing.  It’s important to remember that the whole view gets refreshed every pass of drawRect, so all we essentially need to do is add some radians to the arc drawn every pass.  When we notice that the amount of radians drawn exceeds 2π we know the circle is complete and reset the display link.

And that’s it!  If you run that code, you should see the circle fan out at a constant rate.  You could clearly make this slicker by seeding the display link on didAddToSuperview or adding an easing function to the calculation for radiansToDraw.  I hope this has been useful to some folks out there.  As always I’ll post the code to github so you can check it out.

Thanks and happy coding!

Using Patterns in Quartz2D

There’s not a ton of information out there about how to use patterns with the Quartz framework and drawing patterns, especially about combining it with UIColors ability to create colors with patterns.

You probably know about creating UIColors with pattern images:

UIColor *myPattern = [UIColor colorWithPatternImage:[UIImage imageNamed:@"mypattern.png"]];

If you’re just filling in background colors with this, it works fine, but if you need to get down into the nitty gritty using CoreGraphics, you’ll need to do some more work. Here’s what you’ll need in the drawing code to make it all work:

CGContextRef ctx = UIGraphicsGetCurrentContext();
CGColorRef color = [myPattern CGColor];
CGColorSpaceRef colorSpace = CGColorGetColorSpace(color);
CGContextSetFillColorSpace(ctx, colorSpace);
if (CGColorSpaceGetModel(colorSpace) == kCGColorSpaceModelPattern)
{
    CGFloat alpha = 1.0f;
    CGContextSetFillPattern(ctx, CGColorGetPattern(color), &alpha);
}
else
{ 
    CGContextSetFillColor(ctx, CGColorGetComponents(color));
}

This is the kind of code you might us drawRect: for a UIView. We first need to determine if we’re painting a color or an image which is determined by examining the color space in line 5.

If the color space model is not a pattern, we can set the fill color and simply move along. Otherwise we need to set the fill pattern. Using an alpha value for the components is described in the CGContext docs:

components
If the pattern is an uncolored (or a masking) pattern, pass an array of intensity values that specify the color to use when the pattern is painted. The number of array elements must equal the number of components in the base space of the fill pattern color space, plus an additional component for the alpha value.
If the pattern is a colored pattern, pass an alpha value.

Since we’re using a colored pattern image, we can simply use the alpha value.

That’s it! Hope this helps somebody out there!

EDIT: Sample project is now on GitHub

Also, here’s an easy online tool to create your own pattern: StripeGenerator

Drawing with CADisplayLink

For 90% of the animations you’ll ever need to do CoreAnimation or UIView animations are enough. Sometimes, the thing you want to animate can’t be handled by simple property based transitions or by moving an object along a path. Enter CADisplayLink:

From the CADisplayLink class reference:

A CADisplayLink object is a timer object that allows your application to synchronize its drawing to the refresh rate of the display.

The purposes that I need, I want to animate a circle similarly to how flex charts draws pie charts (a “swipe open” kind of animation). For the demo, though, I’ll simply use the CADisplayLink to animate a line extending across the screen on a CALayer using it’s drawInContext: method.

First the main view that contains my CALayer subclass:

//  MainView.h

#import <UIKit/UIKit.h>
#import <QuartzCore/QuartzCore.h>
#import "AnimationLayer.h"

@interface MainView : UIView 
{
    AnimationLayer *alayer;
}

@end
//  MainView.m

#import "MainView.h"

@implementation MainView

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        // Initialization code
    }
    return self;
}

- (void)didMoveToSuperview
{
    if ([self superview])
    {
                
        alayer = [[AnimationLayer alloc] init];
        alayer.frame = self.frame;
        [self.layer addSublayer:alayer];
        
    }
}

And next the CALayer that will be animated:

//  AnimationLayer.h

#import <Foundation/Foundation.h>
#import <QuartzCore/QuartzCore.h>

@interface AnimationLayer : CALayer 
{
    CADisplayLink *displayLink;
}

@end
//  AnimationLayer.m
#import "AnimationLayer.h"

static bool _running;

@implementation AnimationLayer

- (id)init
{
    self = [super init];
    if (self)
    {
        displayLink = [[CADisplayLink displayLinkWithTarget:self selector:@selector(setNeedsDisplay)] retain];
        [self setNeedsDisplayOnBoundsChange:YES];
    }
    
    return self;
}

static CGPoint lastPoint = {0, 0};
- (void)drawInContext:(CGContextRef)ctx
{
    if (!_running)
    {
        [displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
        _running = YES;
        return;
    }

    CGContextSetStrokeColorWithColor(ctx, [[UIColor blackColor] CGColor]);
    
    CGRect rect = CGContextGetClipBoundingBox(ctx);
    
    CGContextMoveToPoint(ctx, 0, 0);
    lastPoint.x = lastPoint.y += 1;
    CGContextAddLineToPoint(ctx, lastPoint.x, lastPoint.y);
    CGContextStrokePath(ctx);
    
    if (lastPoint.x == rect.size.width)
    {
        [displayLink invalidate];
        _running = NO
    }
}

@end

The main view is pretty uninteresting as it only creates a new CALayer and adds it as a sublayer of itself.

The animation layer holds the CADisplayLink as an Ivar and initializes it with the layer’s drawInContext: method.

We need some kind of flag to tell the CADisplayLink when to stop updating, for this I used a static BOOL _running. On the first pass of drawInContext, we’ll add the display link to the main run loop, at which point it will start calling the specified selector. We’ll set the _ruinning flag to true and return. On every subsequent pass of drawInContext: we’ll draw a longer line running diagonally. At the end we’ll check the condition to invalidate the CADisplayLink; in this case I’ll check to see whether the line has fully crossed the screen. Once we invalidate the display link the animation will stop.

This is a simple example, but it shows how you can animate an object using pure drawing for the times when property animations simply won’t do.