NSWindow, it seems, was never designed with the aim of supporting animation, which becomes slightly problematic when that's precisely what you're trying to do. This is the result of all the research I've done on NSWindow in the past few days because I was trying to do some animations with it, which proved to be a nigh impossible task.
So, what can you do in terms of animation with NSWindow? NSWindow supports a few basic animations - opacity, frame size, frame origin and frame rotation. If you want to change any of those and have the window animate the action, it will gladly do so. You can do those either using NSViewAnimation, through the setFrame:display:animate: method and perhaps even through the animator proxy. Try this Quickie to help you get started with simple window animations.
Beyond that short list, the next best thing you can do is to create the impression that you are animating the window. This allows your animations to be more flexible but not as smooth and well-blended as they would be were they being performed on the window itself. One way to animate things is using the Core Animation API that was introduced with Leopard. What you need to do to get things started though is a bunch of CALayers which Core Animation can animate. Now, these layers need to be inside something and, generally speaking, anything that you want to show on the screen needs to be inside a window. So, the solution is to create a transparent window the size of the entire screen and use that as your canvas, set that window's contentView to have a backing Core Animation layer and then add images of your windows (but not the windows themselves of course) as CALayers to this big transparent layer as sublayers. Here's some code that demonstrates that:
I don't know whether you picked up on it or not but I never autoreleased or released the NSBitmapImageRep, which was for a reason. The reason is that the CGImageRef we get back from the NSBitmapImageRep is actually half-hearted and is directly dependent on the NSBitmapImageRep, so much so that if the NSBitmapImageRep goes away and you try to access the data in the CGImageRef, your program crashes or you get junk on your screen. I'm currently working on it and trying to find a solution to this, but, for the time being, a memory leak it shall remain!
Anyway, at the end of that code segment, you have a CALayer that looks exactly like the NSWindow (minus the shadow) and can be animated at will. All you have to do now is to hide the actual NSWindow, animate your fake window, and, at the end of the animation, unhide the actual window and make sure it's where the fake window was last seen. Complicated, but so far the only way I've found to "animate" NSWindows.
So, what can you do in terms of animation with NSWindow? NSWindow supports a few basic animations - opacity, frame size, frame origin and frame rotation. If you want to change any of those and have the window animate the action, it will gladly do so. You can do those either using NSViewAnimation, through the setFrame:display:animate: method and perhaps even through the animator proxy. Try this Quickie to help you get started with simple window animations.
Beyond that short list, the next best thing you can do is to create the impression that you are animating the window. This allows your animations to be more flexible but not as smooth and well-blended as they would be were they being performed on the window itself. One way to animate things is using the Core Animation API that was introduced with Leopard. What you need to do to get things started though is a bunch of CALayers which Core Animation can animate. Now, these layers need to be inside something and, generally speaking, anything that you want to show on the screen needs to be inside a window. So, the solution is to create a transparent window the size of the entire screen and use that as your canvas, set that window's contentView to have a backing Core Animation layer and then add images of your windows (but not the windows themselves of course) as CALayers to this big transparent layer as sublayers. Here's some code that demonstrates that:
- (void)awakeFromNib {
/* screenWindow is an IBOutlet hooked up to a borderless
NSWindow window and this stuff should ideally be done
by subclassing NSWindow and making a TransparentWindow
subclass and putting this code in its -awakeFromNib */
[screenWindow setBackgroundColor:[NSColor clearColor]];
[screenWindow setHasShadow:NO];
[screenWindow setOpaque:NO];
[screenWindow setFrame:[[NSScreen mainScreen] frame] display:YES];
[[screenWindow contentView] setWantsLayer:YES];
/* Get the layer from this empty window */
NSView *rootView = [screenWindow contentView];
CALayer *rootLayer = [rootView layer];
/* Create the layer that will animate */
CALayer *fakeWindowLayer = [CALayer layer];
/* Get the window's (another IBOutlet) contents */
NSBitmapImageRep *imageRep;
[rootView lockFocus];
imageRep = [[NSBitmapImageRep alloc] initWithFocusedViewRect:[rootView frame]];
[rootView unlockFocus];
/* Now set the layer's contents and add it to the layer tree */
fakeWindowLayer.contents = (id)[imageRep CGImage];
[rootLayer addSublayer:fakeWindowLayer];
/* ... Now do some animation with this layer ... */
}I don't know whether you picked up on it or not but I never autoreleased or released the NSBitmapImageRep, which was for a reason. The reason is that the CGImageRef we get back from the NSBitmapImageRep is actually half-hearted and is directly dependent on the NSBitmapImageRep, so much so that if the NSBitmapImageRep goes away and you try to access the data in the CGImageRef, your program crashes or you get junk on your screen. I'm currently working on it and trying to find a solution to this, but, for the time being, a memory leak it shall remain!
Anyway, at the end of that code segment, you have a CALayer that looks exactly like the NSWindow (minus the shadow) and can be animated at will. All you have to do now is to hide the actual NSWindow, animate your fake window, and, at the end of the animation, unhide the actual window and make sure it's where the fake window was last seen. Complicated, but so far the only way I've found to "animate" NSWindows.
1 comments

