Joris Kluivers

I like to build software.

Interface Builder and Custom NSViews

My previous post was about JKDistributedView, my custom layout view I use in one of my applications. In my layout code I loop thru all subviews added in Interface Builder to calculate their sizes. I still don’t know why but today an error occured that wasn’t there before. The initial layout was totally messed up and instead of three views layed out vertically some random gaps seemed to appear between the views. It appeared JKDistributedView didn’t have three subviews like I added in Interface Builder but it had four subviews. And even stranger, on the next layout the fourth unknown subview was gone all the sudden. After some debugging I found out what this fourth subview was.

Loading Resources: What Happens When a Nib File is Loaded
When you drag a standard object into the design window, Interface Builder encodes the actual class for that object, initializing the object as it builds the nib file. For custom subclasses of such objects, Interface Builder encodes the standard object but tells the archiver to swap in your custom class when the object is unarchived. These objects are also initialized when the nib file is built. Therefore, when the nib file is loaded, initWithFrame: is not called for either standard objects or custom subclasses of standard objects.

On the other hand, because the actual code for your subclass of NSView is not resident in Interface Builder, it does not encode the actual subclass when you drag a custom view object into the design window. Instead, it encodes a special object called NSCustomView that knows how to build the real custom view subclass. After NSCustomView has been instantiated by initWithCoder:, it calls alloc and initWithFrame: for the real class, and swaps in the real instance.

So when your nib file contains a subclass of NSView this class will be initiated using initWithFrame: by a NSCustomView instance that was saved in the nib file as a temporary replacement for your custom NSView.

Normally you shouldn’t need to worry about NSCustomView but JKDistributedView contained a custom view and my layout code was performed before the temporary NSCustomView for this custom view had the time to clean up itself. The NSCustomView was layed out like all other subviews causing an empty space to appear. This was easily fixed by skipping all NSCustomView instances in the layout code:
if ([subview isKindOfClass:NSClassFromString(@"NSCustomView")]) { 
continue; /* skip this loop */
}