Ever wanted to be able to use fancy transitions and animations in your Xcode applications? This series provides the method and the means to implement terrific flips and ripples throughout your app, showing window transition effects using undocumented and documented Objective-C code that can be implemented anywhere quickly and efficiently.
The Static Preview
Some of the more simple animations that we’ll be starting to get into in later parts of this series.
A little more animated
Pardon the pun. The following are some more examples of animation in both undocumented and documented code. (Quicktime is required to view these video previews). In this series of tutorials, we’ll be making projects similar to these. (Eventually. This first part, as I mentioned, is an introduction. The next few sections consolidate on this and get increasingly more spectacular, which is to be expected of course.)
CoreGraphics
The Cube Animation Demo using CGSPrivate.h was made in forty lines of written code. The actual application moves back and forth between the tabs, and closes with a nifty animation upon reaching the end of the tabs. After reading this tutorial, you should be able to write a similar app.
CoreImage
Made using 100% documented CoreImage filters, the CoreImage Animation Demo was written in a little over 60 lines of code. You’ll notice animations like the ripple effect that you get on the Dashboard are able to be implemented using completely documented code. There are also exploding effects, page flip effects and a laser effect! This method is absolutely stunning, but requires more code than the CoreGraphics method. We’ll be visiting CoreImage in the next part of the tutorial.
CoreImage Animation Demo (3.1 MB)
That’s the preview, now let’s get coding!
Animations and Transitions using CoreGraphics
Introducing CGSPrivate.h
Richard J Wareham wrote up an excellent header file for “undocumented CoreGraphics stuff”, and this “Core Graphics” header file (View) will be the basis for all our animations in this first part of the tutorial.
Download the CGSPrivate Header File
If you do a search for any of these functions in Google or anywhere else, chances are you’ll come up with few, if any results. The reason for this is that CGSPrivate.h is exclusively an internal API for Apple. Regardless, you will see many implementations of this in the Mac environment, with the cube effect during Fast User Switching being quite popular. The header file includes functions for creating, running and discarding animations, which means that there is relatively little work for us to do in order to get our animations happening. You may want to download the file now as we’re going to use it later on in the tutorial.
Apple have neither released, nor endorse the functions CGSPrivate.h. Being undocumented, if Apple change this code then our animations will no longer work; the chance of that happening? Close to zilch.
The functions for workspaces can be ignored, unless you want to write another alternative to Desktop Manager or VirtueDesktops. However, the functions that we will be utilising are mainly CGSNewTransition, CGSInvokeTransition and CGSReleaseTransition. They should be pretty self explanatory, but I’ll go through them just to make sure we’re on the same wavelength.
CGSNewTransition creates a transition object with the ID of a CoreGraphics connection, options in the form of CGSTransitionSpec (transition type, transition direction, the window to apply it on, and the background color we want), and the handle by which to refer to the transition.
CGSInvokeTransition runs the transition with the specified handle for a certain period of time.
CGSReleaseTransition releases the transition with the specified handle from memory.
The Project
Project One - Cube Animation Through an NSWindow subclass
This project will be a simple window with a button on in that invokes a cube animation when clicked. It’s not designed to be useful, rather it will be our first animation project from which we’ll get the knowledge of CGSPrivate and it’s functions. We’ll create apps that actually do something a little on. The best way to learn is by example.
Let’s start up Xcode and create a basic Cocoa Application project.
Call the project whatever you want, I’ve called it “animation”. Now you’ve get a screen with a mostly empty project.
Building the interface
You know the drill, go ahead and bring up MainMenu.nib in Interface Builder, and subclass NSWindow. I’ve called my custom class AnimWindow. Bring up the inspector and add the an action “turn:” which we’ll be using to animate our window.
I expect you know how to create files for the AnimWindow class and make the custom class of the NSWindow to AnimWindow. While you’re at it, you can also drag a button onto the window and connect it to the turn: action we just added. Save and close the nib, and let’s head back to our dear friend Xcode.
I like to move it, move it
We’ve set the interface, but nothing happens when we run our application. It’s time to get our hands dirty and delve into the code.
As I mentioned earlier, we’re going to be using CGSPrivate.h, so download the file and add it to the project. Once the file is added to the target, open up the AnimWindow.m file and import CGSPrivate.h at the top of the file. This allows us to access all the functions found in the CGSPrivate header file. We’ll now add code to the “turn:” action to make the window flip around. Replace the existing blank action with the following code:
- (IBAction)turn
id)sender
{
// declare our variables
int handle;
CGSTransitionSpec spec;
// assign our transition handle
handle = -1;
// specify our specifications
spec.unknown1=0;
spec.type=CGSCube;
spec.option=CGSLeft;
spec.backColour=NULL;
spec.wid=[self windowNumber];
// Let’s get a connection
CGSConnection cgs= _CGSDefaultConnection();
// Create a transition
CGSNewTransition(cgs, &spec, &handle);
// Redraw the window
[self display];
/* Pass the connection, handle,
* and duration to apply the animation
*/
CGSInvokeTransition(cgs, handle, 3);
/* We need to wait for the transition to finish
* before we get rid of it, otherwise we’ll get
* all sorts of nasty errors
*/
usleep((useconds_t)(3000000));
/* Finally, release all our variables */
CGSReleaseTransition(cgs, handle);
handle=0;
}
This time, when you Build and Run, the whole window should turn into a cube and spin around when you click the button. That’s a full fledged animation in 15 lines of written code!
We deal with getting rid of the black background later. For now, let’s examine the code we’ve just executed.
Post mortem - What exactly did we do?
The famous cube animation was implemented by a simple subclass of NSWindow, with only 15 lines of written code. We’ll start at the top, and go down through our entire AnimWindow.m file and see what’s going on at each particular instant.
AnimWindow.m
#import “AnimWindow.h”
#import “CGSPrivate.h”
We tell Xcode to include AnimWindow.h, and CGSPrivate.h. As I’ve reiterated a thousand times, CGSPrivate.h contains the definitions for the functions that give us access to the CoreGraphics API for transitions and animations.
@implementation AnimWindow
- (IBAction)turn
id)sender
{
// declare our variables
int handle;
CGSTransitionSpec spec;
// assign our transition handle
handle = -1;
Here we simply declare the variables that we will use later on in the method. The integer handle is what we use to refer to our animation object, especially when we have more than one.
We also declare “spec” as a CGSTransitionSpec. This will hold all the options for our transition, such as transition style and direction.
Lastly, we set the handle integer to -1. This can take any value, but usually it is best to leave it 1 or -1.
// specify our specifications
spec.unknown1=0;
spec.type=CGSCube;
spec.option=CGSLeft;
spec.backColour=NULL;
spec.wid=[self windowNumber];
We set up our transition’s options. CGSTransitionSpec takes the parameters of unknkown1, type, option, backColour and wid.
- Unknown1 is best left at 0 until someone finds out what it’s for.
- Type refers to the type of transition. We’ve put in CGSCube here, but it can be anything from the CGSTransitionOption integers defined in CGSPrivate. For example, you can change it to CGSFlip or CGSSwitch and see what happens when you run the project again.
- Option defines the direction of the transition. Again, all the directions are defined in the CGSPrivate header file. We’ve used CGSRight here, but CGSLeft, CGSUp and CGSDown are all valid directions.
- BackColour (with UK English spelling for some reason) is obviously the colour that the rest of the screen becomes during the transition. This takes an NSArray with three floats to specify the RGB values for the desired color. Sending Null, as in our case defaults to black. We will look at getting rid of the backColour in the second example.
- Wid takes the window number of the NSWindow that we want to apply the transition to. We can get the window number of any window by passing the windowNumber method. In this example, we are applying the transition from the window to itself, so we pass a [self windowNumber];
// Let’s get a connection
CGSConnection cgs= _CGSDefaultConnection();
To run any function of CGSPrivate, we need to have a connection to CoreGraphics. Here we define cgs as a _CGSDefaultConnection(). In actual fact, we could omit this step and pass _CGSDefaultConnection() in place of cgs when we do so later on, but doing this step means less typing afterwards and easier to follow code.
// Create a transition
CGSNewTransition(cgs, &spec, &handle);
Obviously creates a new transition. We pass on the connection (cgs), the specifications (spec) and the handle. We use the handle to refer to this transition rather than making a pointer to it, like we would most objects in Cocoa.
// Redraw the window
[self display];
Forces the window to update. If we don’t redraw the window, the transition will happen, but any changes we wanted to come up will be shown after the transition. Not much of a problem in this sample since we don’t have any content on our window, but we’ll be getting to that.
/* Pass the connection, handle,
* and duration to apply the animation
*/
CGSInvokeTransition(cgs, handle, 3);
Applies the transition. This is the line that causes the whole world to go black and the window to “cube”. We need to pass the cgs connection we made earlier, the handle to identify which transition to invoke and finally the duration in seconds. For a smooth transition, duration is generally best left as an integer between 1 to 10.
/* We need to wait for the transition to finish
* before we get rid of it, otherwise we’ll get
* all sorts of nasty errors
*/
usleep((useconds_t)(3000000));
/* Finally, release all our variables */
CGSReleaseTransition(cgs, handle);
handle=0;
}
@end
We need to release the memory used up by our transitions, but if we release anything while the transition is still going, we’ll muck things up. For this reason, we tell the system to wait for the transition to finish by the usleep function. We pass whatever duration we had times 1000000. In our case, 3 * 1000000.
The next line releases the transition from memory. We just need to pass the handle to identify which transition we’re interested in removing, and that’s a wrap! Congratulations, you just pulled off a CoreGraphics animation in 15 lines of written code!
Download Source
Getting Serious
The sample project in this first tutorial was great to demonstrate the use of CGSPrivate, but it could have been written better. In the next part of this series, we’ll be to putting our knowledge to work, and creating an app that actually does something. Stay tuned.
Our Second Project - Setup Assistant (coming soon)
Was this article useful?
Let me know if you found this useful, for motivational purposes. Any feedback or criticism is welcome to improve the scope of this article, and remember, part two is coming soon, which expands on our current knowledge and introduces some new concepts; not to be missed for the world.

38 Comments so far
Leave a commentPages: « 1 [2] Show All
This means nothing. Apple can (and does) have the facility to change those applications to compensate for changes in the private API.
It is a horrible idea to use private API.
recorded by Scott Anguish on October 23, 2006 4:19 pm | Permalink
Scott: You’re right that Apple could easily change the private APIs, but if they do, it wont be before Leapord. It’s not an easy task to rewrite the whole API and then update the OS to accommodate those changes. Fast user switching and expose aren’t applications; they’re built into Tiger. If any changes do occur, they’ll be in OS 10.5.
Even so, assuming that Apple do change Core Graphics, (even though there is really no reason to) then changing your code is a simple matter. It would be silly to expect a complete revamp of the APIs since transforms will still be transforms, whether you accomplish them one way or another.
Think on the bright side, Quicksilver, Virtue Desktops, Desktop Manager and even Adium (yes, Adium X uses Core Graphics) rely on these APIs, and any changes necessary would be highlighted pretty fast. I’m sure almost all Mac users use Quicksilver constantly, and it really wouldn’t be the same without it’s cube, fade, and resizing effects that it accomplishes through these functions.
Having said that, I’m not promoting the use of internal functions, just providing the knowledge for people who want to experiment with them.
So, Apple could easily change these functions if they wanted to, though there is no real feasible reason for them to do so, and even if they did, the changes would not be significant enough to cripple your applications.
What it all boils down to in the end, is if you’re comfortable using private APIs, you’re welcome to use them. If you’re not, there’s no harm getting a head start into learning them for when Apple do decide to release them.
expressed by Ankur on October 23, 2006 4:44 pm | Permalink
But that makes the assumption that they will be released. And that isn’t necessarily so. The transition from private API to public API isn’t just a “publish the header” situation.
Just because something won’t break until Leopard doesn’t mean that it is a good idea to do it. And it could change in the interim… so it is still a bad idea.
BTW, I have experience with this process (i.e. guess where I work).
I think the idea of the tutorial is great. and more core graphics/core image tutorials are needed, but private API is bad mojo.
composed by Scott Anguish on October 25, 2006 3:57 pm | Permalink
You’re right. What I meant was, if and when they’re released.
Now you’re doing it
(Assuming).
Scott, I agree with what you’re saying, but as I said, apps like Quicksilver really wouldn’t be the same without Core Graphics, and if Apple change their code developers can change their code too.
Absolutely. In the same way, upgrading the API isn’t just an “upgrade the API” situation. If there were upgrades, they wouldn’t rewrite the code to wreck current dependant apps since Tiger depends on it as well. No way are they going to update the OS just to fit some upgrade to an internal API which is fine the way it is.
Could you by any chance, uh… take my resume?
Thanks. I respect your views, and I can see where you’re coming from. Again, this article is just informative, so if developers want to be like Virtue or QS, they don’t have to feel left out.
Good to hear your criticisms, and thanks for airing your opinions.
Ankur
Excuse the exceedingly long reply.
recorded by Ankur on October 25, 2006 4:51 pm | Permalink
Very good tutorial Ankur! The guy from Argentina is not the only one from the far south, I’m from Chile. Please keep this good blog rolling
published by Kurt on November 2, 2006 8:15 am | Permalink
Thanks for the feedback Kurt, I appreciate your response. The blog will keep rolling as long as I have something to offer the community.
stated by Ankur on November 2, 2006 3:35 pm | Permalink
I’m curious about what parts are entirely private, and what could I do with transitions that would still be documented?
stated by Jason Terhorst on November 6, 2006 2:18 pm | Permalink
Hi Jason,
If you’re after documented transitions, you can make use of several Core Image filters; although Core Graphics is a lot smoother. For some sample source code, check out this post. It includes both Core Graphics (undocumented) and Core Image (documented) transitions.
voiced by Ankur on November 6, 2006 3:06 pm | Permalink
You’re right. CoreGraphics tends to respond quite a bit quicker - the transition occurs almost immediately. With Core Image, my computer pauses for half a second or so to “think” about what it needs to do first.
recorded by Jason Terhorst on November 6, 2006 3:20 pm | Permalink
I attempted to add a QTMovieView and play a sample movie file, and then hit the button to see the cube animation, and I was secretly hoping that I could see the movie still play during the animation. Alas, that didn’t happen. Is there any way to see the contents of a window update visually while the animation is working?
announced by Jason Terhorst on November 6, 2006 3:43 pm | Permalink
If you’re using the above code, first of all in the turn: action change
spec.option=CGSLeft;tospec.option=CGSLeft | (1<<7);. That justs gets rid of the black background, which can get quite annoying.Then, remove
[self display]. Let me know what happens. There’ll probably be a slight flicker.announced by Ankur on November 6, 2006 3:56 pm | Permalink
Actually, scrap that. I just tried it, and it’ll still pause the screen. I think your best bet would be to use Quartz to render a cube, on which you easily put any movies, images or whatever.
mentioned by Ankur on November 6, 2006 4:02 pm | Permalink
The CGSFlip and CGSSwitch transitions that you mention do not work as they are not defined in the enum in the header-file. However, a dashboard-like flip transition can be obtained by setting spec.type = 9
announced by PGM on November 7, 2006 2:51 am | Permalink
Hey, thanks for picking that up. I didn’t even notice.
Actually this header file is a little out of date. The Flip and Switch transitions are defined in my Core Graphics Framework.
reasonded by Ankur on November 7, 2006 9:59 am | Permalink
Core Graphics actually is Apple’s official name for all of Quartz, not just the undocumented parts. The two names are interchangeable. That’s why everything Quartz-related starts with “CG”.
I’ve no idea what the S in CGS stands for. Secrets?
expressed by Peter Hosey on December 4, 2006 5:28 pm | Permalink
Hi Peter, CGS is just my way of saying Core Graphics - the ‘S’ indicating the plural, although a lot of this code is definitely under wraps.
proclaimed by Ankur on December 4, 2006 6:02 pm | Permalink
This was a really interesting, I’m still waiting for the next step in the series.
disclosed by mandaris on May 19, 2007 10:28 am | Permalink
Can you please tell me how you block the interface?
stated by WD-NYC designer on June 20, 2007 2:46 am | Permalink
WD-NYC Designer, what do mean by block the interface? Freeze the window or black out the screen?
professed by Ankur on June 20, 2007 4:17 pm | Permalink
A quick question:
would it be possible to implement a rotation of the screen in a clockwise/counter-clockwise direction?
ie a workaround for displaying the screen in portrait mode for those nvidia display card owners (like 12″ powerbook) who dont’ get the rotate option in the dispay preferences.
the CGSSetWindowTransform method sounds intriguing as a possible solution.
Or if you can control the cube transition to rotate on a different axis.
published by Nick on July 26, 2007 10:33 pm | Permalink
Nick, there are plenty of different directions which the cube can rotate. The
spec.optionin the above post can take values such as CGSLeft, CGSRight, CGSDown, etc. - but it doesn’t spin clockwise / anti-clockwise.recorded by Ankur on July 27, 2007 12:27 am | Permalink
First, thanks for this great example.
Now I have one question:
What needs to be done that the shadow of the window remains during the transition? Perhaps you can post some code…
spoken by Tom on September 4, 2007 1:31 am | Permalink
soo .. how do you get rid of the black background?
transparency would be be oh so cool!
voiced by hypereye on April 27, 2008 12:19 pm | Permalink
Keeping the shadow would be difficult, but transparency:
uttered by Ankur on April 27, 2008 10:14 pm | Permalink
Hey Ankur,
Still working on that new tutorial with core image? plus do you freelance build cocoa applications?
stated by Bash on June 26, 2008 2:45 am | Permalink
Pages: « 1 [2] Show All
Leave a comment