Smooth animation in your Dock icon

Code and wisdom in this article have not been kept up-to-date. Use them at your own peril.

Customizing your application’s Dock icon with a smooth animation, in two easy steps.

If you are simply replacing your application’s icon, then things are pretty easy: load (or generate) your animation frames as CGImageRefs, and use SetApplicationDockTileImage.

However, if you are trying to composite the animation frames on top of your existing application icon, it gets a little more interesting. BeginCGContextForApplicationDockTile does nothing to restore your original application icon, so if you try to draw two frames in succession using BeginCGContextForApplicationDockTile, you’ll end up with both of them on top of you application icon, which is probably not what you wanted.

OverlayApplicationDockTileImage has exactly the same problem — if you call it twice in a row, it overlays both images on top of your application icon.

RestoreApplicationDockTileImage is supposed to help with this problem, but, unfortunately, if forces the original application icon to be drawn in the Dock immediately, before you’ve had a chance to composite your next animation frame. This results in unsightly flicker.

Clearly, every time you advance to a new frame, you need to draw the application icon and then composite the appropriate animation frame, without flushing the application icon without the animation frame first. For example:

// Dock icons are 128x128
CGRect          iconRect = CGRectMake(0, 0, 128, 128);
CGContextRef    context = BeginCGContextForApplicationDockTile();

// Erase whatever may be in the dock icon now
CGContextClearRect(context, iconRect);

// Draw the app icon first
PlotIconRefInContext(
    context,
    &iconRect,
    kAlignNone,
    kTransformNone,
    NULL,
    kPlotIconRefNormalFlags,
    appIconRef
);

// Draw your animation here

CGContextFlush(context);
EndCGContextForApplicationDockTile(context);

You can get your application icon with:

// Get a CFURL for the appname.icns file
CFURLRef    icnsURL = CFBundleCopyResourceURL(GetMainBundle(), CFSTR("appname"), CFSTR("icns"), NULL);

// Get the FSRef for the icns file
FSRef        icnsRef;
CFURLGetFSRef(icnsURL, &icnsRef);

// Get the icon from the icns file
IconRef        appIcon;
GetIconRefFromFile(icnsRef, &appIcon, NULL);