Tuesday, January 30, 2007

Free Noob Lesson

Unless you have an infinite stack and care not about results, don't call [someView display] inside [someView drawRect:].

Bad dog. No bone.

Sunday, January 21, 2007

CSS FTW

Kam Dahlin asked about my code formatting for the NSViewAnimation post, and I was embarassed to report that I had no special css style for it. So, my friend Mitch stepped up and handed me this:

.source_code, li .source_code {
padding-left: 2px;
border-left: 1px solid #666;
font: 100% Monaco,monospace;
font-size: 8pt;
white-space: pre; /* CSS2 */
white-space: -moz-pre-wrap; /* Mozilla */
white-space: -hp-pre-wrap; /* HP printers */
white-space: -o-pre-wrap; /* Opera 7 */
white-space: -pre-wrap; /* Opera 4-6 */
white-space: pre-wrap; /* CSS 2.1 */
white-space: pre-line; /* CSS 3 (and 2.1 as well, actually) */
}

/* change this line to set the width of box */

.source_code {
width: 100%;
}

/* change this line to set the width of box in a list */

li .source_code {
width: 100%;
}

Stick that in your css file, and invoke it by surrounding the code text with <div class="source_code"> and </div>. You still have to detab the code first, but it beats the hell out of some &nbsp's. Also note that Blogger will not display the style when you preview a post- you have to submit the post to see it.

Update: Use "pre" instead of "div" and all is well. I can fly through asm but html is a black art to me. Non-thanks to all who failed to correct me earlier :P

Saturday, January 13, 2007

a.out of gas

Gruber says it's not surprising that Apple has OS X compiling and running on something other than PPC or x86. Considering that <mach/machine.h> lists 14 possible processor families, I have to agree with him. But what I find most interesting, assuming that the iPhone uses an ARM CPU, is that Apple is able to target one of the processors that is commented out in that file. If they're targeting ARM, maybe they're using 'gas' from binutils instead of 'as' from cctools...

Friday, January 12, 2007

Fun With Custom Keys

I was playing around with NSViewAnimation and I wondered why there weren't more predefined dictionary keys. Then I realized that it was just an associative array*, and I could define as many keys as I want, and take the appropriate action in a delegate method. This is probably obvious to most of you, but I just discovered it. So I gotta, you know, blog it.

Here's an example I threw together that lets you swap one view for another after animatedly resizing a window. The most obvious use of this would be in a preference window, when switching between multiple tabs. For the example names, I'm using Wolf Rentzsch's 'NSX' naming convention.

// Define custom objects and keys
#define NSXViewAnimationSwapAtEndEffect @"NSXViewAnimationSwapAtEndEffect"
#define NSXViewAnimationSwapOldKey @"NSXViewAnimationSwapOldKey"
#define NSXViewAnimationSwapNewKey @"NSXViewAnimationSwapNewKey"

Then somewhere in your controller class:

    NSRect  newWindowFrame  = someCalculatedRect;

// Build a dictionary...
NSMutableDictionary* windowDict =
[NSMutableDictionary dictionaryWithCapacity: 5];

// ...with standard keys...
[windowDict setObject: myWindow
forKey: NSViewAnimationTargetKey];
[windowDict setObject: [NSValue valueWithRect: newWindowFrame]
forKey: NSViewAnimationEndFrameKey];

// ...and custom keys.
[windowDict setObject: NSXViewAnimationSwapAtEndEffect
forKey: NSViewAnimationEffectKey];
[windowDict setObject: someViewToHide
forKey: NSXViewAnimationSwapOldKey];
[windowDict setObject: someViewToShow
forKey: NSXViewAnimationSwapNewKey];

// Create the animation.
NSViewAnimation* windowAnim = [[NSViewAnimation alloc]
initWithViewAnimations: [NSArray arrayWithObjects:
windowDict, nil]];

// Go.
[windowAnim setDelegate: self];
[windowAnim startAnimation];
[windowAnim autorelease];

And finally, the delegate method:

- (void)animationDidEnd: (NSAnimation*)animation
{
// Standard sanity checks.
if (![animation isKindOfClass: [NSViewAnimation class]])
return;

NSArray* animatedViews =
[(NSViewAnimation*)animation viewAnimations];

if (!animatedViews)
return;

// Grab the window from the first animation.
NSWindow* animatingWindow = [[animatedViews objectAtIndex: 0]
objectForKey: NSViewAnimationTargetKey];

if (animatingWindow != myWindow)
return;

// Iterate through possibly multiple animations.
UInt32 i;
UInt32 numAnimations = [animatedViews count];
id animObject = nil;
id keyObject = nil;

for (i = 0; i < numAnimations; i++)
{
animObject = [animatedViews objectAtIndex: i];

if (!animObject)
continue;

// Grab our custom effect key.
keyObject = [animObject objectForKey: NSViewAnimationEffectKey];

if (!keyObject)
continue;

// Execute the custom effect.
if ([keyObject isEqualToString: NSXViewAnimationSwapAtEndEffect])
{
NSView* oldView = [animObject
objectForKey: NSXViewAnimationSwapOldKey];
NSView* newView = [animObject
objectForKey: NSXViewAnimationSwapNewKey];

if (oldView)
[oldView setHidden: YES];

if (newView)
[newView setHidden: NO];
}
}
}

A trivial example, no doubt, but there are countless possibilities. Imagine calling setDuration: from the animation:didReachProgressMark: delegate to implement some crazy animation curve. Or overriding setCurrentProgress: in a subclass to link several animations more intuitively than with startWhenAnimation:reachesProgress:. Of course, all this is possible without using custom dictionary keys, but this way is a lot more fun.

I'd love to hear any more uses you can think of, and other classes that use dictionaries in a similar way.

*I originally said 'it was just xml', and Peter Hosey pointed out that while the key/object relationship evoked feelings of xml in me, it's not really xml until it's saved to disk. I've updated the text to more correctly describe data in memory.

Wednesday, January 10, 2007

Score One For Curious George

My sources in Washington D.C. report that Dubya went to great lengths to schedule tonight's address. From planting moles inside Apple to buying iPods for all of the most influential journalists, no expense was spared. George's plan was executed without a hitch, and his most controversial address to date was carried out at exactly the perfect time.

Here's what was going through his head while he read from the teleprompter:

"I have decided to say 'Fuck You' to 65% of Americans, Gawd knows how many Iraqis, and whoever those guys are who did that report thingy. And probly Congress, too. Since you're all shitting yourselves about the iPhone, you're not even listening to me! Here it comes, bitches- iRock '07! Woohoo! No dictator? No WMDs? Who cares?! We got OS X on a phone!"

Yeehaw, people. 20,000 iRock fans can't be wrong.

Friday, January 5, 2007

The Year of the HIG

Apparently, when Gruber speaks, people listen. "The HIG Is Dead", his presentation at C4, has sparked an incredible amount of discussion and activity recently. Amidst all the individual stories of developers sprucing up their UIs, Brandon Walkin has organized the Indie HIG. I highly recommend everyone take a look, and get involved if you like the idea. Also, I have an account there, and that makes it even cooler.

It can't be expected to actually replace Apple's HIG, and Brandon says as much on the front page. It's largely a design principle popularity contest, with a bunch of dev types discussing do's and don'ts and occasionally agreeing with each other. And it will never have the authoritative feel of Apple's official docs. Having said all that, it's the best idea I've heard recently, and there's no way around those issues. Unless we can hypnotize Steve Jobs...

Which brings me to another point- Apple's HIG 2.0. I predict this will be the year we see it. Not until WWDC, maybe, but I think Apple is paying attention. I believe the volume of public complaints from developers about the difficulty of maintaining a consistent look and feel has already surpassed Apple's threshold of tuning-out. There have been rumors of more standard controls in Leopard, and standard toolbar icons, etc. and the HIG 2.0 will be a perfect companion.

In the meantime, join us over at the Indie HIG, and throw in your 2 cents. Imagine the stories you can tell your grandchildren when your ideas get rolled into the HIG 2.0...

UPDATE
My first-ever prediction may be comin' up Milhouse. Daniel Jalkut gives me hope. And no, I did not attend the tech talk when it rolled through my town, which was before I posted my prediction.

Wednesday, January 3, 2007

Can't Touch Nibs

I heard something recently on the grapevine that I haven't yet seen in the blogosphere, so I thought I should mention it. If there's any demand, I'll dig deeper into the code and post further details.

- Open up Interface Builder, create and save a new blank nib file(Cocoa/Application).
- Locate the new nib in Finder, and 'Show Package Contents'.
- Delete 'classes.nib' and 'info.nib', but leave 'keyedobjects.nib'.
- Attempt to reopen the modified nib package in IB.
- Marvel at IB's reluctance to open the file for editing.

Ok, so IB won't open it, so what?

It turns out that IB's 'Open' command and [NSBundle loadNibNamed:owner:] have entirely different requirements regarding the contents of the nib package we're attempting to load/open.

The reason this came to light was that someone was trying to have a look at XCode 2.4.x nibs, which lack the 2 files we deleted above. I'm not sure when Apple started deleting those 2 files, but on my box(XCode 2.2) I have all 3 internal files in 'PBX.nib' and 'PBXActiveOperationsPanel.nib'. But obviously, since XCode 2.4.x launches with no errors, those 2 deleted files aren't needed at runtime.

So, at first glance, the moral of the story is- you can keep your users from opening your nibs in IB(thus keeping those damned GUI hackers at bay) by simply deleting the aforementioned files from your nibs, possibly with a custom build phase.

But just for fun, we glanced twice.

- Repeat the 'create and save a blank nib file' steps, but this time, don't delete anything.
- Copy 'classes.nib' and 'info.nib' from the newly created nib package into the unopenable nib package.
- Marvel at IB's willingness to open the previously unopenable file for editing.

I am sure that there is some information lost in the translation, but this seems to be a good start. As always, any comments/questions/suggestions are welcome.