Working on the settings page of the application I’m developing at work I stumbled across a rather annoying characteristic of NSScrollViews. The NSScrollView allows you to fit a larger custom view into a smaller view which the user then can scroll up/down/left/right to see all the content in the custom view. For our application we need more space to fit all the settings and so I put the controls into an NSScrollView. The problem that occurs is that when you load the page with the NSScrollView it starts at the bottom-left of the custom view inside. This is pretty irritating, every implementation you see of a scroll view starts at the top left – it’s what everyone expects.
So off to google and Stack Overflow I go, apparently this a very frequently brought up question and as such there’s a solution: flip the view. However, this has rather unfortunate consequences for the poor developer (me!) and an awful lot of the mailing lists and posts I’ve read on the subject seem to suggest that we have to man up and get on with it. I find this rather odd, because the solution I’ve come up with is stupidly simple and I cannot be the only one to figure it out. Read on for an example of the problem and my solution.
Disclaimer: I’m using Leopard on a mac mini, Xcode & Interface Builder versions 3.1.2. As these are the only versions I’ve used I cannot point out any differences you may encounter.
1) Let’s start off with the initial problem. Create a new Application Project in Xcode. Load up the MainMenu nib/xib in Interface Builder.
2) Double click the Window to open it up. Drag an NSScrollView onto it and stretch it to fit the window. Now select the NSView inside the NSScrollView. Change the width and height to be 100 pixels greater than the window’s values. Now place 4 labels in the NSView. Drag each label to a corner and name them “Top Left”, “Top Right”, “Bottom Left”, “Bottom Right” to match their location.
3) Run the Xcode project. The window will appear as below:
As you can see, it’s loaded the view pinned to the bottom-left corner.
4) Now for the solution to the first problem: Go into Xcode and create a new objective-c file, I’ve called mine FlippedView. Make it a subclass of NSView. Put this code into the .m file:
5) Now in Interface Builder select the NSView inside the NSScrollView and go to the Identity Inspector. In the Class box select your new NSView subclass. Now run the Xcode Project. You’ll get this:
We’re at the top-left of the NSScrollView, but we’ve flipped everything inside it top-to-bottom. Not so good.
6) Now we have two choices. a) we move our labels around in Interface Builder so that it appears in the right place (and any images we use still get flipped). Or b) Fix it. I prefer the latter, so let’s do that.
7) Select all of your labels (in the window, not the component list) and go to Layout -> Embed Objects In ->Custom View. A new NSView should be inserted into your NSView subclass and all of the labels will be inside the new NSView. Resize the new NSView to fit the NSView subclass and voila:
And for good measure, here’s the final list of components for the nib/xib: