This article is more than 1 year old
Cloud power harnessed to dump unwanted Mac apps
Five PNGs, one engine
Mac Secrets One of the many cool things about the Mac is the attention to detail that's been put into the user interface.
For example, when you drag something out of the Dock, or customize a toolbar by removing an item from it, you'll see a little explosive "poof" animation, accompanied by an appropriately destructive sound.
Well, here's how to do same thing for yourself, customizing the animation as appropriate.
At this point, some folks will say that it's possible to actually do this in a documented manner thereby undermining what I'm about to say. From Panther onwards, the official way of creating a "poof animation" is to use the AppKit routine NSShowAnimationEffect.
This routine takes a number of arguments including an integer that indicates the wanted effect. This can take only two values - zero or 10. I wondered if it were possibly to use other integer values in order to get alternative animations, but such attempts are ignored. The poof animation is the only one that's present.
It seemed to me that it would be useful to make use of the underlying animation "engine", if only one could find a mechanism for supplying different images for the animation itself.
The "poofing" code in the Dock application uses five separate frames within a single PNG
After some digging around, I discovered that NSShowAnimationEffect simply makes use of an undocumented class, NSToolbarPoofAnimator. As the name suggests, this class is used by the toolbar to implement poof animation effects. Incidentally, in case you're wondering, the Dock doesn't use this mechanism - it has its own built in "poofing" code.
The most relevant NSToolbarPoofAnimator method is this one:
+ (void) runPoofAtPoint: (NSPoint) point withSize: (NSSize) size callbackInfo: (NSDictionary *) callbackInfo;
This class method takes a point where you want the animation to be displayed, a size for the animation, and a call-back dictionary. This dictionary can be nil, but if it's used, it should contain the following keys and objects:
Key: AnimationEffectDelegate
Object: The delegate to be notified when the animation ends.
Key: AnimationEffectDidEndSelector
Object: A string representation of the delegate selector to call
Key: AnimationEffectContextInfo
Object: An NSValue object containing a pointer to optional contextInfo
If this doesn't make sense, carefully read Apple's documentation on NSShowAnimationEffect. You should be able to see how the above three keys enable the NSToolbarPoofAnimator code to notify your delegate when the animation is over.
NSToolbarPoofAnimator works by loading five images into the _poofAnimFrames array, one of the instance variables within the class. These images are named "NSToolbarPoof1", "NSToolbarPoof2" and so on. You can easily view them by just dropping an image view control into Interface Builder, and then setting the image name to the aforementioned strings.
The bad news is that these named images are built right into AppKit itself - they're not even visible as discrete resources within the framework. This means we can't replace them with our own images - right? Wrong.
It turns out that this is very easy to do. Initially, I tried simply creating a new Cocoa project and adding five image resources to the project, with the names given above. I figured that [NSImage imageNamed:] would look inside the application bundle before going to the AppKit framework. This approach didn't work - I suspect certain image names receive special treatment.
In the end, I sub-classed NSToolbarPoofAnimator and added a little extra functionality. My code relies on the fact that the various images get loaded inside the following method:
- (id) initAtPoint: (NSPoint) point withSize: (NSSize) size callbackInfo: (NSDictionary *) callbackInfo;
Once this method has been called, the aforementioned _poofAnimFrames array has been populated with the standard poof images, and there's absolutely nothing to stop us replacing those images with our own.
To try this out, I added the following method to my subclass:
- (void) replaceImagesWithBaseName: (NSString *) baseName { NSInteger idx; for (idx = 0; idx < 5; idx++) { [_poofAnimFrames [idx] release]; NSString * imageName = [NSString stringWithFormat: @"%@%d", baseName, idx + 1]; _poofAnimFrames [idx] = [[NSImage imageNamed: imageName] retain]; if (_poofAnimFrames [idx] == nil) NSLog (@"Warning: image %@ not found!", imageName); } }
Given a specific base name (such as "Fred") this method locates the image resources "Fred1", "Fred2" and so on, and plugs them into the _poofAnimFrames array.
At this point, I should confess that I am severely challenged from an artistic perspective. Rather than trying to come up with an animation of my own, I borrowed Apple's Dock animation and used Photoshop to give a blue tinge to each "poof bubble".
You can see the result when you run the demo program. I'm not encouraging you to copy Apple's artwork - the intention here is to demonstrate how to animate your artwork using a subclass of the NSToolbarPoofAnimator class.
While I was at it, I thought it would be fun to provide a mechanism for reversing the direction of the animation. Depending on what custom animation you want to use in your own application, this could be useful to indicate that some previous operation has been undone.
You can see how this works by consulting the source code of the demo application, which can be downloaded from here. ®