Three Ways To Load A Nib. Only Two Work.

Someone I’m working with told me that you can load a NIB on the iPhone in the following way:

TestViewController *c = [[TestViewController alloc] initWithNibName:@"TestNib" bundle:[NSBundle mainBundle]];
[self presentModalViewController:c animated:YES];

However, this doesn’t appear to work. When chasing down the code, by the time TestViewController -(void)viewDidLoad gets called, self has changed; however, deep inside the lazy load of presentModalViewController the old pointer seems to be used because I get an exception indicating the view reference of the underlying UIViewController (that TestViewController descends from) was uninitialized.

There are two other ways to load the Nib.

First is using -(NSArray *)loadNibNamed:owner:options:

NSArray *a = [[NSBundle mainBundle] loadNibNamed:@"TestNib" owner:self options:nil];
TestViewController *c = [a objectAtIndex:0];
[self presentModalViewController:c animated:YES];

This seems to work, though the top level objects have been offset by one since v2.0 of the iPhone OS. (That is, this code will only work on v2.1 or later; if you build for v2.0 you must add one to the objectAtIndex index.)

The second way is the recommended way given in Apple’s documentation here:

// In my top level class
@interface TestRetainViewController : UIViewController 
{
    TestViewController *fController;
}
@property (nonatomic, retain) IBOutlet TestViewController *fController;
[[NSBundle mainBundle] loadNibNamed:@"TestNib" owner:self options:nil];
[self presentModalViewController:fController animated:YES];

This assumes there is a reference in the TestNib.xib file which points from the file owner object to the NIB.

One hitch about the second method, using an array: the array that is returned has a reference count of 1, but also has been set to autorelease. Thus you should either retain the array or the items in the array you want. The hitch about the third method, of course, is that because you’ve declared a property with retain semantics, you must also release the object on dealloc:

- (void)dealloc
{
    [fController release];
    [super dealloc];
}

You also can test to see if the NIB was already initialized in the call to loadNibNamed, however, in theory during NIB loading the reassignment to fController should release whatever was already loaded there before.

What tripped me up about the last project I worked on was that I was in such a rush to get very complicated NIBs built for an over-engineered UI, I created circular references and (more importantly) I wasn’t releasing any of the objects loaded via the NIB loading process. Turned out object connections on the iPhone are established using -setValue:forKey:, which does an automatic retain, and that means you need to release the reference in the -dealloc method.

What’s worse, if you don’t explicitly create the @property (or equivalent setXXX call according to the key/value rules, I believe simple assignment with retain is called–which means if you do multiple calls to loadNibNamed: above on TestNib but without the @property declaration, you’ll leak as each call simply overwrites the pointer.

The lifespan of an object owned by NSMutableArray, or why Objective-C annoys me and Java makes me happy.

Hard to find memory bug on the iPhone.

I had to find it by (a) building the map file for the distribution of the iPhone product, and (b) walking for an hour around Santa Monica (pretty day, though a bit on the warm side) waiting for the map software component to crash.

I finally found it. It’s the sort of memory bug that Java programmers never have to concern themselves with.

So suppose you’re writing a queue, and you use an NSMutableArray. (In Java you’d use a LinkedList object, which would be faster, but I digress.) Removing the object from the NSMutableArray, I wrote:

MyObject *obj = [myMutableArray objectAtIndex:0];
[myMutableArray removeObjectAtIndex:0];
UseMyObject(obj);

And all is right in the world, correct?

Um, not quite.

See, there is thing thing called the autorelease pool. It’s quite clever: it holds a list of all the objects that, after this current event is finished being processed, the objects will be released and potentially deleted from the system. So when you get a return value back from some function, unless its a function which explicitly gives you ownership of the object, you can safely use that object until the end of the event processing loop, which certainly won’t happen until after your routine returns, right?

Um, not quite.

NSMutableArray notes in its documentation that on a call to the remove function removing that item from the array, release is called immediately on the object, which means my object obj above has been released. Now what makes this a hard to find bug is that while the object may have been deleted and its memory released, the object itself still lingers in memory unless you use a memory debugging library.

And unfortunately for me (1) the conditions causing this routine to be called is only triggered when walking around with our new software package, and (2) the various memory debugging libraries only work under the iPhone simulator.

The workaround?

MyObject *obj = [myMutableArray objectAtIndex:0];
[[obj retain] autorelease];  // before removeObjectAtIndex
[myMutableArray removeObjectAtIndex:0];
UseMyObject(obj);

Feh.

This is exactly the sort of stuff that garbage collection helps with, and is the primary reason why I like Java: no messing with object lifecycle management. Unfortunately garbage collected Objective C isn’t available on the iPhone, which means that so long as I’m trying to write Objective C code that runs on the iPhone and may also run on the Macintosh, I cannot use garbage collection in my Objective C development.

iPhone SDK v2.1 gotcha.

The easiest way to load a NIB containing some element of your user interface on the iPhone is to call the UIKit addition to NSBundle loadNibNamed:owner:options:.

So throughout my code I have the following:

NSArray *nib = [[NSBundle mainBundle] loadNibNamed:@"MyNIBFile" owner:self options:nil];
UIViewController *c = [nib objectAtIndex:1];

Um, there’s a problem here. On return the NSArray returned contains at index offset 0 a reference to the bundle; the first defined object is at 1 and so forth.

That is, on the iPhone 2.0 SDK.

Using the iPhone 2.1 SDK, the index starts at 0; a reference to the bundle is not returned. Thus, you need to change the statement above to:

UIViewController *c = [nib objectAtIndex:0];

Meh.

Naturally, of course, if I had used the iPhone SDK like the MacOS SDK, I would have set the file owner in the NIB to an object which contains IBOutlets for the items in the NIB being loaded. And had I done this, I wouldn’t have had a problem at all.

It’s why God created tool tips.

Don’t hide or disable menu items.

This is what separates the User Interface men from the User Interface boys: the suggestion “Instead, leave the menu item enabled. If there’s some reason you can’t complete the action, the menu item can display a message telling the user why” is the most Gawd-aweful suggestion I’ve heard in a long time.

The correct answer, of course, is to create a tool tip which pops up over the disabled item which indicates why the item is disabled. That way we have instant feedback on what a user can do, and if the user is puzzled, he can easily discover why a particular item isn’t currently selected.

In Java, you can do this by calling JMenuItem.setToolTipText(); set the value to an explanation why the menu item has been disabled, and the explanation will pop up when the user hovers over the disabled item.

On the Macintosh, with Cocoa you can do this by setting the ToolTip text field in the IB .xib file with the text describing the item; call NSMenuItem’s setToolTip: method in order to update the text for the disabled menu item.

And even Windows has the ability to create tool tips for disabled menu items, though it takes a hair more work: by watching for WM_MENUSELECT notifications you can then pop up a CToolTipCtrl, or update a status bar text line, showing the appropriate text if the item is disabled.

So as much as I appreciate Joel’s comments on the software development industry, on this particular item, ummmmm… No. I agree more with John Gruber: you’re much better assuming your users are clever. But if your menu command structure contains commands which are just odd or hard to puzzle out at first glance, tool tips are much better than an idiot modal dialog box. It’s just more elegant.

Java sucks and Objective-C is great? Puuuhhhllleeeeaaassseee…

I still remember the brouhaha raised over Java on the Macintosh, and pronouncements by many of the Macintosh Technorati that Java sucks. (I believe Gruber’s words were Cross-platform crippity-crap Java apps.)

By all of the various posts I’ve seen, I’d think that Java was a complete wasteland while Cocoa was the rolling green hills of programmer nerdvana.

Okay, that’s fine. I set forth building a new custom application for the retail market, and faced with the various choices for building a cross-platform system I decided to build a simple data core in C++ with the Mac UI in Objective C++ and Cocoa, and the Windows UI in MFC/C++. (The common data core is to assure that data can be easily shared, but is a very small kernel: perhaps right now about 10% of the overall code base. So I’m not trying the YAAF-lite solution of building a UI framework on top of Objective C++ or MFC; rather, I’m building the UI twice, with a common set of data accessor routines at the bottom of the stack.)

Nerdvana? Hardly.

Today’s fun-filled afternoon was spent trying to figure out how to do an easy gradient fill for a custom control so it doesn’t have a flat, 2D appearance. With the sheer beauty of the Macintosh UI environment, you’d think would come convenience routines–but not really: the amount of work to build a gradient filled background was about what I’d expect using the Graphics2D class in Java.

And I’ve come to a couple of conclusions while engaging in this little exercise.

(1) The advantages outlined in Jens Alfke’s essay about making superior interfaces only gets you half-ways through the front door. To make it across the finish line requires a lot of nit-picky detail work that the Macintosh Cocoa libraries only sorta help you with. Sure, there is plenty of support for animation stuff which is really freakin’ cool. But to draw my simple gradient in a way which was portable back to MacOS 10.4 (and my business app needs to be supported by the current version and the previous version of MacOS X) required about 120 lines of code–which, while it took a couple of hours to toss together and test, wasn’t the easy exercise that many Cocoa advocates seem to suggest awaits all who come to the Cocoa big tent.

This isn’t to say that there aren’t advantages to Cocoa over Java Swing or MFC. However, there are many places where Java’s framework has a clear advantage: the JScrollPane class, for example, is a lot more flexible to work with than the NSScrollView class. And don’t even get me started on NSTreeController, though Rentzsch’s comments on enabling NSZombieEnabled was a god-send.

A corollary to this is:

(2) Macintosh applications look great because Macintosh programmers sweat the details, not because the Macintosh environment makes sweating the details any easier than in Java or Windows MFC. It could very well be the Macintosh market: people expect pretty applications, and so a lot of attention goes into making pretty applications on the Macintosh. This attention to detail, however, doesn’t really exist in the Windows market–and don’t even get me started on Enterprise Java applications. (I will agree with Gruber on this: a lot of Java Enterprise Swing applications do look like crap.)

However, this does not mean that the look and feel of Java applications are doomed, any more than it means Cocoa applications are uniformly great–nor does it mean to get the same level of greatness on a Java application you must spend two or three times more effort than on a corresponding Cocoa application. (And it’s not like Cocoa has inherently better support for creating icons like Panic’s beautiful icons, as opposed to the type of 1980’s icons you see in Java: nowadays they’re both PNG files.)

This attention to UI detail, by the way, predates Cocoa, and even predates NeXT, as anyone who ever read the original Apple Macintosh Human Interface Guidelines would attest. In fact, if anything, NeXT programmers and Cocoa programmers prior to the Apple merger weren’t exactly producing stellar examples of UI goodness: I’d say the biggest problem that I saw as an outsider when Apple and NeXT merged was having Apple’s attention to UI detail and usability drilled into the NeXT programmers–much to their shock. Even today I’d say that Apple’s attention to UI detail is only about 90% of what it used to be in the Mac OS System 7 days.

And that attention to UI detail wasn’t because Mac OS System 7 was a fantastic object-oriented development environment. (I should know; I was writing software for System 6 and earlier.) It was because you would sweat details like the fact that on System 6, an OK button was offset 14 pixels–not 15, not 13, not 8–from the lower right of the screen and was 58 pixels wide, not 57, not 59. (Now I will say that System 6 was superior in one aspect to Microsoft Windows–a superiority which existed for quite a few years: Macintosh QuickDraw’s CopyBits() routine was light-years ahead of any bit blitter available on Windows or on X windows: it would anti-alias, crop, stretch, extrapolate, and even fold your laundry and put it away for you, giving the Mac OS System 6 platform a clear advantage over Windows 3.1. But Windows and X windows caught up in that department long ago.)

So anyone from the Macintosh Technorati who suggests that Cocoa is inherently superior–I’m used to religious wars, even from people who should be intelligent enough to know better.

Oh, and for the curious, I’ve attached my GraidentFill class in Objective C using the CGShading routines to handle my custom control shading. It’s really not as flexible as the 10.5-only NSGradient class, but it does the trick.

NSOutlineView

I’m spending a lot of time now with my new friend, the AppKit object NSOutlineView. Right now I’m trying to implement drag and drop, which is easy if you actually look for the documentation for NSOutlineViewDataSource, which, silly me, isn’t mentioned in the “Drag and Drop Programming Topics for Cocoa.” Instead, the documentation outlines how to create drag and drop for tables–and silly me, knowing that the NSOutlineView inherits from NSTableView, started writing the data methods for table drag and drop instead of outline view drag and drop.

The other thing that is extremely disappointing are NSViewControllers. When I first read about them I thought “oh, cool; the perfect class thingy to insert into the view hierarchy to provide the functional equivalent of a NSWindowController but for just a subclass of the window view hierarchy!”

Uh, no.

First, the NSViewController only exists for 10.5 as far as I can tell–and any application development I want to do should be 10.4 backwards compatible. (Many corporations such as Yahoo! are still using 10.4 internally, as corporations tend to be about one to two years behind in deploying new operating systems.) Second, it seems NSViewController objects are designed to allow and control views that are “out in space”–that is, that are not associated with a window, or that are programmatically associated later on with a window.

To do what I want to do, I simply created a custom class and hooked the responder chain in my class. It’s ugly as hell–but it’s exactly what I want: a way to handle just a subset of controls within a region of my window to properly manipulate the events. (Think of a group of radio buttons or check boxes which change the state of other radio buttons or check boxes in a group box of controls–at a higher level you may just want the abstraction “I’m in state ‘N'”–which translates to some combination of check boxes being enabled or disabled at the lower state. By placing the functionality of translating “N” into the state of a bunch of controls, I’ve isolated key functionality somewhere else–which is, to me, the whole point of object oriented programming: hiding the ugly details in reusable classes.)

Things I’ve learned while playing with AppKit

Every framework I have ever encountered, every API, Toolkit, or whatever, consists of (a) a specification, (2) an implementation, and (3) a whole bunch of bugs which require experience to understand and work around. In many ways one could say that the difference between one who is just learning a framework and one who understands the framework is in part measured by how many bugs one “just knows how to work around.”

Here is what I’ve learned so far about Apple’s AppKit:

(1) If you want to make Apple’s synchronized scrolling hint work, you need to make sure the document views across the different scroll windows have the same frame origin. In order to guarantee this, the following snippet of code seems to work for me:

- (id)initWithFrame:(NSRect)frame 
{
	/* Kludge: Modify the origin to be 0. This forces the value to be 0 so
	 * we can do synchronized scrolling across multiple desparate objects.
	 * Otherwise, we don't have a common origin, so things don't synchronize
	 * as the value of the origin is essentially random from the nib.
	 */
	
	frame.origin.y = 0;
	frame.origin.x = 0;
	
    self = [super initWithFrame:frame];
    if (self) {
         ... the rest of your initialization goes here ...
    }
    return self;
}

(2) (And the subject of my last rant post) If you embed a your own custom view within an NSScrollView, if your view implements either -(NSView *)headerView or -(NSView *)cornerView, you’ll get either a horizontal header or a corner view a’la the NSTableView’s documentation on both of these methods. (In other words, NSScrollView handles and manages the NSClipView for the table view by seeing if the document implements these methods.) Handy if you need a custom header along the top of your document view–though honestly, if you’re creating a table or a drawing program, you’re better off using the built-in NSTableHeaderView or NSRulerView classes instead.

For example:

- (NSView *)headerView
{
	if (fRuler == nil) {
		NSRect r;
		
		r.origin.x = 0;
		r.origin.y = 0;
		r.size.width = 320; // default width; resize frame with correct width as frame resizes
		r.size.height = 16;
		
		fRuler = [[ResourceRulerView alloc] initWithFrame:r];
	}
	return fRuler;
}

(3) Oh, and to answer the off-asked question how do you get your content view document to grow from top down rather than from bottom up, and how to stick to the top:

(a) Implement -(BOOL)isFlipped in your custom view. Return YES. Yes, this flips the coordinate system so the upper left corner is the origin, instead of the lower left corner. No, I don’t know how to do this without flipping the coordinate system: I’m new to this whole thing. 🙂

(b) Make sure you flip the autosize parameters as shown below:

Disclamer: All of these things seemed to work for me. However, I reserve the right (as always) to be a complete idiot and get the details wrong.

The Objective-C/Java Flame Wars: Uh, huh?

Several months ago there was a flame war amongst many of the better read technical blogs, and from what I read in places such as Daring Fireball and Thought Palace was that Java sucked–and part of the reason why it sucked was that Java Swing sucked.

By extension, of course, the Objective-C Application Framework was a very well thought out framework designed by people who knew how to do user interface design–and beautiful user interface design–as opposed to those neanderthal Java developers at Sun who, in a massive language land grab, built a user interface environment which was so horrific that it doesn’t even entertain being taken seriously as a user interface API.

Okay, I will admit Swing has its places where the designers made some really bad design decisions. The whole Swing pluggable L&F environment, while that may be cool in theory on platforms like Linux (where the idea of a design language is either C or C++), but really blows on Windows or on the Macintosh as it requires extra work to make your application fit in rather than look like a refugee from a 1980’s Motif concentration camp. And the fact that it took Sun until v5.0 of Java to realize that floating windows aren’t a bad thing–a fact that some Window Managers still haven’t figured out, combined with a lack of a reasonable serialization standard–okay, I could bitch about Swing on and on and on. (And please don’t get me started on keyboard shortcuts or the fact that you have to write extra code to make the escape key cancel a modal dialog box.)

But now that I’m starting to crack the mysteries of the Objective-C Application Framework, I’m not exactly seeing the nerdvana of a perfect and well designed UI construction environment created by experienced user interface designers.

To whit, I give you–NSScrollView.

I only have three words for those who think the Objective C Application Framework is superior to Java Swing: What The Frack?

First, NSScrollView really really wants to pin your document to the lower left, according to the Application Framework’s insistence on using a mathematically correct coordinate system with the origin to the lower left. That’s great for the 5 programmers out there who are creating statistics and mathematical plotting packages. But for the rest of us, who are creating text and textually-based packages, flipping the Y axis isn’t just an exercise in mathematically imprecise masturbation. Documents–and by extension computer displays–tend to be read top down. Scrolling tends to focus on top-down reading. Documents should start at the top and go down.

And don’t give me this “just use -isFlipped;” nonsense, as without knowing the magic size settings to fiddle in the Interface Builder, when you have a document that is bigger than the window, on resize you’ll find that your document expands upwards, not downwards like every fracking window out there. (You need to select your custom view, then set its size in the inspector to pin to the lower left instead of the upper left. By pinning it to the lower left, after the coordinate system is flipped, document resizing will fix the upper left as you expand your window. Yes, that made no sense. But it works.)

But that’s not what brings me here today; oh, no. I can live with the quirkiness of some stupid design decision made early on that now everyone winds up living with because it’s too deep in the underlying framework to fix. And God knows there are plenty of these in any framework, where you wish some early on design decision could be taken back, but it’s too late now, and now everyone has to just learn the quirk to get along with your code.

No, what I want to talk about is creating custom horizontal and vertical headers.

In Swing, this is a relatively simple proposition: when building your JScrollPane, you also create a view which manages your custom column header, then add it with jScrollPane.setColumnHeaderView(). This may make creating a table view a little harder–you have to tell the JScrollPane holding your table to use the table view passed to it from your constructed table view item, but it’s all fairly well documented in the Sun “How to use Tables” docs. And for the small percentage of us who want to roll our own tables–because we have a complex drawing task we need to accomplish or because we need to draw something that is almost, but not entirely like, a table–well, we can just build a document view and a header view, and pass them to JScrollPane.

So I went to figure out how to create my own custom header view for my NSScrollView. Okay, perhaps I was going about it the wrong way: perhaps I should have just constructed a table view then used a very large hammer to bang the damn thing into the precise shape I wanted. However, that’s not what brings me here either.

What brings me here is the following snippet of code which I ganked from GNUStep. (As a footnote I find that, for understanding the fringe cases and the assumed design models of an application framework–and every application framework has underlying assumptions as to how the pieces will be strung together which seem obvious to the initiated, but are often completely undocumented by the developers.) What’s great about GNUStep is that, at least for the core elements, at least you can look at the code and learn how things are actually built under the hood, so when gaps show up in Apple’s documentation (and Apple’s documentation has gaps that you can drive a truck through–which disappoints me, given how great pre-NeXT Apple docs were), you at least have a fighting chance to figure out what’s going on without wasting a day.

And here is what I found:

- (void) _synchronizeHeaderAndCornerView
{
  BOOL hadHeaderView = _hasHeaderView;
  BOOL hadCornerView = _hasCornerView;
  NSView *aView = nil;

  _hasHeaderView = ([[self documentView] 
                        respondsToSelector: @selector(headerView)]
                    && (aView=[(NSTableView *)[self documentView] headerView]));
  if (_hasHeaderView == YES)
    {
      if (hadHeaderView == NO)
        {
...

In other words, deep in the bowels of NSScrollView, in order to allow an NSTableView to build a custom table column header, NSScrollView gets the current document being scrolled–and if that document responds to headerView, uses that view as the header.

And notice that unnecessary cast to NSTableView. Not to belabor the point here, but what the hell is this? In any case where you have an undocumented (at least in NSScrollView) hook, mentioned in passing in NSTableView, which provides a hook for giving a header to your custom view–to me, it’s a sure sign of a hack. A kludge. A piece of code tossed in (and this is not GNU’s fault–GNUStep is simply implementing to the best of their ability the OpenStep specification on which Cocoa is based) in order to support a specific case behind the scenes–the need to build a custom table view–rather than creating a general solution then implementing the specific instances for specific cases.

WTF?

Any time in your class hierarchy where you use “supernatural knowledge” of another class–and one of the things that irritates me about Objective-C is how easily someone can create hooks which creates this “supernatural knowledge” through querying an object to see if it implements a poorly documented hook–its a sure sign of something that congealed, rather than something that was designed.

And while that wouldn’t matter much to me: after all, any software project is about making compromises to get something out the door–it wouldn’t irritate me quite so much if the accusations of poor design weren’t leveled so fiercely by those who think Objective C is the epitome of good UI framework design. And I speak as someone who once built my own framework.

Feh!

So… How many languages can you use?

Let’s see: Java + JSP for the server. C++ for the core desktop and phone engine. C++ for the Windows and WinCE version. Objective-C++ for the Mac and iPhone version.

Sounds just about right for a custom client-server system targeting multiple platforms: it’s about using the right tool for the job, and keeping things as simple as they can be–but no simpler.