Animated NSWindow resizing in 10.3 or later
I recently expanded a custom class I have to provide animated window resizing under 10.4. This is to provide a smooth change during style changes in the new 4.0 version of Lotus Meditation Timer (coming soon!).
I thought it might be fun to chop that class down even further into a simple, all-in-one method that provides animated window resizing without requiring 10.5 or 10.4, and has no need for callbacks or animation delegates.
A couple quick caveats:
- The animation is blocking, no events in your app are processed while the window resizes
- When providing a new rect for the window, pass the desired content size, do not adjust for the NSWindow’s titlebar, as this is handled automatically
- To adjust framerate & speed, just change the appropriate variables inside the method
- The BBWA_ prefixes are arbitrary and can be ignored or renamed
- To avoid requiring 10.5, NSThread’s -sleepForTimeInterval is replaced with a simple usleep call, which requires that you #include <unistd.h> in your project, if it is not already. Or, you can use NSThread’s -sleepUntilDate, if you don’t want to use usleep
- This method will ignore your target rect’s origin, and focus only on the window’s new size, keeping the window centered where it was on the screen
- I wrote this up pretty quickly to post here so please forgive any mistakes or sloppy coding practices. I have tested this method, however, and it works quite well (for me), but if anyone has a problem with it let me know and I’ll be sure to update it
Easy as A-B-C. Here’s the code:
-(void)resizeWindow:(NSWindow*)theWnd toRect: (NSRect)r
{
NSRect BBWA_startRect = [[theWnd contentView] frame];
BBWA_startRect.origin = [theWnd frame].origin;
//grab the center point
NSPoint cp = NSMakePoint(BBWA_startRect.origin.x +
(BBWA_startRect.size.width / 2.0),
BBWA_startRect.origin.y +
(BBWA_startRect.size.height / 2.0));
NSSize ts = r.size; //target size
NSSize ss = BBWA_startRect.size;
//for controlling speed of animation:
float timeDur = .5; //in secs
int frames = 30; //number of updates
//
float progress;
int i;
float pauseTime = (timeDur / (float)frames);
NSRect frameRect;
NSRect adjusted;
NSSize dif = NSMakeSize( ts.width - ss.width,
ts.height - ss.height);
NSSize adjSize;
for(i=1;i<=frames;i++)
{
progress = ((float)i/(float)frames);
adjSize = NSMakeSize(dif.width * progress, dif.height * progress);
frameRect = NSMakeRect(cp.x - ((adjSize.width + ss.width) / 2.0),
cp.y - ((adjSize.height + ss.height) / 2.0),
adjSize.width + ss.width,
adjSize.height + ss.height);
adjusted = [theWnd frameRectForContentRect: frameRect];
[theWnd setFrame: adjusted display: YES];
usleep((pauseTime * 1000000));
}
}
And that’s all there is to it! Using that method you can quickly and easily resize an NSWindow and do it in a nice, animated, Mac-friendly way. And you don’t even need Tiger.
Cheers~