Sunday, May 27, 2012

Blocks (7)

As long as I was thinking about NSAnimation (here), I wrote a command-line program that uses it. This is a minimal example.

In the process, I realized that the callback can be done with a block. I'm not sure what the approved method to do this would be; I looked at the delegate methods and they're not promising. I guess if we were instantiating the animation from an object we'd just pass self as an argument to the alternative init method. In a GUI app we'd use an IBOutlet and never need to do the explicit init in our code.

Output:
> ./prog
2012-05-27 12:08:12.794 prog[7813:707] Progress: 0.20
2012-05-27 12:08:12.994 prog[7813:707] Progress: 0.40
2012-05-27 12:08:13.194 prog[7813:707] Progress: 0.60
2012-05-27 12:08:13.394 prog[7813:707] Progress: 0.80
2012-05-27 12:08:13.595 prog[7813:707] Progress: 1.00

// clang prog.m -o prog -framework Cocoa -fobjc-gc-only
#import <Cocoa/Cocoa.h>

typedef void (^B)(NSAnimationProgress p);
B b = ^(NSAnimationProgress p){ NSLog(@"Progress: %.2f", p); };

@interface MyAnimation : NSAnimation
@end

@implementation MyAnimation

B myBlock;

- (id)init
{
    self = [super initWithDuration:1.0 
                    animationCurve:NSAnimationLinear];
    if (self) {
        [self setFrameRate:5.0];
        [self setAnimationBlockingMode:NSAnimationNonblocking];
    }
    return self;
}

- (id)initWithBlock:(B)b {
    self = [self init];
    myBlock = b;
    return self;
}

- (void)setCurrentProgress:(NSAnimationProgress)progress {
    myBlock(progress);
}

@end

int main(int argc, char * argv[]) {
    NSAnimation *a = [[MyAnimation alloc] initWithBlock:b];
    [a startAnimation];

    NSRunLoop* rl = [NSRunLoop currentRunLoop];
    [rl runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1.2]];
    return 0;
}