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~

Comments are closed.