How to re-position the toggle button in NSOutlineView’s view-based ‘group style’ cell view

Published on 25/05/2012

Along with view-based NSTableView’s, Lion finally introduced what the Apple documentation refers to as ‘group style’ cell views. These have been floating around for a while in application like iTunes and offer users an unobtrusive way of collapsing items in an NSOutlineView.

This is wonderful UI and it’s a pleasure to finally be able to easily implement it in our own NSOutlineView instances. But, as I found earlier this week, things can get a little hairy when you combine ‘group style’ cell views with slightly more complex view hierarchies.

To fully understand the problem, it’s first important to realise that all instances of NSTableCellView (the class you typically subclass to create custom view-based cells) are contained within NSTableRowView instances. When ‘group style’ NSTableCellView’s are instantiated, they’re inserted into their parent NSTableRowView’s along with an NSButton instance. These NSButton instances appear to adopt a height equivalent to that of their NSTableCellView sibling, ensuring that they appear vertically centred.

But as we can see from the following illustration, this only works for simple instances of the ‘group style’ cell view, where the view’s NSTextField is also centred vertically. If we choose to move our NSTextField off-centre, things quickly get ugly.

Unfortunately, for all of my searching, there appears to be no officially sanctioned method of adjusting the ‘group style’ cell view’s toggle button position. Which means, for the moment, we’ll have to adjust it ourselves.

To accomplish this we must first tell our NSOutlineView to make use of a custom NSTableRowView subclass. Thanks to NSOutlineViewDelegate’s handy outlineView:rowViewForItem: method, this is fairly painless:

- (NSTableRowView *)outlineView:(NSOutlineView *)outlineView rowViewForItem:(id)item
{
    CustomTableRowView *rowView = [[CustomTableRowView alloc] initWithFrame:NSZeroRect];
    return rowView;
}

Once we’ve done that, we just need to make our NSTableRowView subclass performs a quick search of its subviews to detect and reposition the NSButton instance:

- (void)drawRect:(NSRect)dirtyRect
{
	[super drawRect:dirtyRect];
 
	NSButton *toggleButton = nil;
 
	// Iterate through our children
	for(id child in [self subviews])
	{
		// Let's assume this is the NSButton we're looking for...
		if([child isKindOfClass:[NSButton class]])
		{
			toggleButton = child;
			break;
		}
	}
 
	if(toggleButton)
	{
		NSRect frame = [toggleButton frame];
 
		/* Make whatever adjustments you'd like here */
 
		toggleButton.frame = frame;
	}
}

Et voila! Nicely positioned ‘group style’ toggle buttons regardless of the complexity of your accompanying cell views.

Nibbble – A Dribbble screensaver

Published on 12/05/2012

About a week ago @markjardine made the following Twitter post:

Someone needs to create a nice quartz composer screensaver displaying dribbble shots. On a related note, I need to learn quartz composer.

As luck would have it, I actually already had a Dribbble-powered screen saver. I’d written it months ago and had been using it ever since. Mark’s tweet prompted me to polish it up and get it ready for release, which I’m doing today! So I’d like to introduce Nibbble – an OS X screensaver that fetches and displays Dribbble’s current popular shots. You can grab it here.

It’s a first release, and as such is fairly green. If there’s enough interest, I’ll look into adding the ability to enter your Dribbble credentials to optionally show you follower shots. Ironically, I’m not much of a graphic artist, so the bundle is without an icon. If someone thinks they can cook up something fun, feel free to drop me a line on Twitter @nialgiacomelli.

If you enjoy Nibbble feel free to repay the favour by checking out my upcoming iOS word game Lumicon.

Nabatar

Published on 6/05/2012

More and more, I’ve found that when I seek out a person online, I’ll look to their Twitter account as a primary means of communication. Why? Because Twitter affords me an instant snapshot of that person at any given time. I know that if I view a Twitter profile I’ll inevitably be able to find that persons website, their current topics of interest and a whole host of other information.

Which is why, when it comes to digital attribution, I’ll usually opt for a Twitter username (as can be seen here). But the problem I’ve always faced is finding a nice way to present that information, which is why today I’m releasing Nabatar.

Nabatar is a simple jQuery plugin that attempts to put names to faces. It automatically detects the inclusion of Twitter and Facebook usernames in text content and converts those mentions to avatar-embedded profile links.

For more information, check out the projects Github page:

Nabatar is a jQuery extension with a simple premise: put faces next to the names of Twitter and Facebook users you mention on your blog. Mention a Twitter user and Nabatar will convert that text into a profile link along with that users current avatar.

Nabatar aims to be fairly lightweight and easily styled. Checkout nabatar.css for an example of how one might go about styling its output.

If you’ve got any questions, drop me a line: @nialgiacomelli.

Lessons from Lumicon: Risk vs. reward

Published on 21/04/2012

It was shortly after sending the second Lumicon beta out to our external testers that we realised we had a difficult decision to make. Things were going fairly well. People had responded positively and we felt we’d found a core mechanic that worked well and was entertaining. The problem was that people were ending games due to fatigue, rather than any kind of mounting difficulty.

It was clear that we had to rethink our approach to difficulty as a whole. But how? The act of assessing difficulty is, in itself, a difficult task. It’s a relative measure. Something impossible to quantify. So ultimately we cast our attention to the game itself. We asked the question, ‘what do we want Lumicon to be?’. The answer, thankfully, was a little easier to quantify:

  • We wanted Lumicon to be accessible, but ultimately frantic.
  • We wanted to encourage tactical play.
  • We wanted to make it very difficult, but not impossible, to play for a long period of time.

At its simplest, Lumicon is a game about making words (for those who haven’t seen any of the in-game shots, they’re on Dribbble). You have a gutter at the bottom of the screen that is gradually populated with letters. Much like a game of Scrabble, you must take the letters that arrive and arrange them to form words. If your gutter fills up, the game is lost.

Approach one: punishing success

Our initial approach to difficulty revolved solely around the speed at which letters were delivered to a players gutter. The faster letters appeared, the more difficult and frantic the game became. It was a simple mechanic and it worked well. As the player created words and ‘popped’ them for points, the speed at which letters appeared would increase. For every 300 points a player scored, the game would move up a notch.

Speeds were capped by bandings that corresponded to the games difficulty level. Each difficulty (Normal, Medium and Hard) would have a minimum and a maximum speed.

Out of the three criteria listed above, our initial system only satisfied one: the fact that we wanted the game to be accessible, but also frantic. Even then, this was only partly true. New players would often approach the game on its easiest difficulty and report little to no challenge. While our default response was to suggest players try the game at a higher setting, it ultimately left a bad taste in our mouths.

The game, we felt, should become challenging regardless of difficulty level. Instead, because of the banding system we’d employed, difficulty would often plateau, leaving players in an awkward limbo. Without mounting difficulty it became possible to play indefinitely, which resulted in players growing restless and ultimately fatigued. Often you would simply allow the gutter to fill up as a means of ending your game.

While there were many issues with this first iteration, by far the most worrying was the fact that it completely failed to satisfy our most important criteria: that the game should reward tactical play. Instead, we found that the banding system actively punished skilled players. Because we were incrementing difficulty based on the number of points a player had scored, it was often better to avoid large combos, as they would inevitably lead to more frantic play.

Approach two: time

We quickly realised that our banding system wasn’t to blame for the games difficulty issues. The culprit seemed instead to be the marrying of difficulty to point score. A situation we attempted to rectify by having difficulty increase linearly over a set period of time.

It was decided that Normal games would start with a letter spawning rate of 3 seconds. Over the course of a full game, this rate would decrease to a minimum of 1.4 seconds. As we had three difficulty levels, we decided that it should take a player roughly 4 minutes of gameplay before a Normal game ‘shifted’ to a speed comparable to that of Medium difficulty.

Which meant that if a player were to start a Normal game they would reach the maximum letter spawn speed of 1.4 seconds in a total time of 12 minutes. Hard players would reach it in 8 minutes, and Advanced in 4.

To make the increase in difficulty as transparent to the user as possible, we implemented a levelling system. Every 45 seconds the players level would increase, along with the letter spawn time. Below is a basic breakdown of the formula used to calculate letter spawn times during each level increase:

slowestLetterSpawnTime = 3.0;
fastestLetterSpawnTime = 1.4;
timeUntilMaxSpeed = 12*60;
levelDuration = 45;
currentLevel = 0;

speedDiff = slowestLetterSpawnTime - fastestLetterSpawnTime;
speedIncrease = (speedDiff/timeUntilMaxSpeed)*levelDuration;
letterSpawnTime = slowestLetterSpawnTime-(currentLevel*speedIncrease);

Given that a Normal game takes 12 minutes to reach our maximum level, and that each level takes 45 seconds, we know that there must be a total of 16 levels in a Normal game of Lumicon.

Using the above formula, we can determine the exact letter spawn time at level 11 by first calculating the speed increase per level and then multiplying that amount by our current level.

speedIncrease = ((3.0-1.4)/720)*45 = 0.099
letterSpawnTime = 3.0-(11*0.099) = 1.9 seconds

And for the sake of completeness, here’s the letter spawn time at level 16 for a normal game:

letterSpawnTime = 3.0-(16*0.099) = 1.4 seconds

Mechanics complimenting mechanics

It’s astonishing to think that by exchanging one metric (player score) for another (total game time) we were able to dramatically increase the difficulty and depth of our entire game. If we return to our initial list of criteria, we can finally appreciate what the above changes allow us:

  • We wanted Lumicon to be accessible, but ultimately frantic. By decoupling player score from difficulty level, and ultimately making difficulty a variable measure, we’ve made it possible for players of any skill level to start a game of Lumicon at any difficulty and eventually find themselves challenged.
  • We wanted to encourage tactical play. By removing score from the difficulty equation, the entire tactical approach of the game is changed. We’ve shifted the impetus away from rationing your combos to avoid difficulty increase, to considering large combos early in the game as a sort of ‘low hanging fruit’. You want to score as highly as possible before difficulty ramps up and it’s far more difficult to do so.
  • We wanted to make it very difficult, but not impossible, to play for a long period of time. While there may ultimately be a select few individuals who find the 1.4 second limit slightly too slow for their liking, we believe the majority of players will find Lumicon’s max speed to be a state of permanent jeopardy.

I can’t wait to see what people make of it.

Lessons from Lumicon: Design

Published on 15/04/2012

For the past three months I’ve been working on an iOS word game called Lumicon with a friend of mine, Glynn Smith. The concept for the game came to me on a Saturday morning and I spent a hurried weekend putting together a basic prototype. The concept was simple, and when I sat down to turn it into a prototype I believed it to be fully formed. It still astonishes me just how mistaken I was.

Even in the beginning, putting together that initial prototype, the entire geography of the idea would shift in my mind. The more I thought about my simple concept, the more uncomfortable it became, the more it seemed to swell. Until eventually I realised that what I was looking at wasn’t a concept at all, but a kaleidoscope of unruly ideas. Each more delicate than the last. Which deserved my attention?

The process of refining those ideas caused me to ultimately reconsider everything I believed about the creative process. Because within my industry, the principles of design and development are approached with a sense of polarity. They are considered as a sort of binary opposition. You are either one, or you are the other. Designers are extrovert, their work is framed so as to be appreciated or scrutinised. Whereas developers are the more introvert souls. Their work is internalised, hidden away behind interface and chrome.

But working on Lumicon helped me to realise that this is largely untrue. It’s easy to see where this misconception originates. Designers create form, developers function. But the reality is that development is an almost entirely external process. You take a concept, something already fleshed out by design documents and mockups, and you turn it into something tangible. Design is ultimately the more internal process. Like the formation of a diamond, it’s the act of applying pressure to ideas until something beautiful is formed. It’s the weighing of options. The solidification of ideas into concepts.

Later, after I’d put together the initial Lumicon prototype, after I’d pitched the concept to Glynn and we’d begun work on it as a fully fledged game, my appreciation for the subtly of game design continued to grow. We would openly discuss design decisions and gameplay obstacles. We would voice our concerns, each giving a sort of ‘State of the Union’ speech on the various aspects of the game we were actively involved in. But it was always after these conversations, working independently, that we’d find time to reflect, to gather our thoughts, and ultimately arrive upon a solution which we would bring to the other.

And the discussions would begin again. Because like a diamond, an idea is often cut rough. It is refined time and time again. But you know what? The game was always better for it.

Calculating screen coordinates from NSView-relative coordinates in Cocoa

Published on 25/01/2012

For a recent release of SpriteRight I set out to integrate snap-to-grid functionality. Unfortunately this required that I calculate screen coordinates from a set of view-relative coordinates.

I’ve decided to make the code I ended up with public in the hope that it avoids someone a little head scratching down the road. It should be noted that the following code makes use of Oscar Del Ben’s fantastic NSScreen+PointConversion category to help with determining the active NSScreen and flipping said screens coordinates.

The following code should handle flipped coordinate systems and multi-monitor displays gracefully. Also included is a function to warp the mouse (without simulating any events) to a specified point in screen coordinates.

- (NSPoint)convertToScreenFromLocalPoint:(NSPoint)point relativeToView:(NSView *)view
{
	NSScreen *currentScreen = [NSScreen currentScreenForMouseLocation];
	if(currentScreen)
	{
		NSPoint windowPoint = [view convertPoint:point toView:nil];
		NSPoint screenPoint = [[view window] convertBaseToScreen:windowPoint];
		NSPoint flippedScreenPoint = [currentScreen flipPoint:screenPoint];
		flippedScreenPoint.y += [currentScreen frame].origin.y;
 
		return flippedScreenPoint;
	}
 
	return NSZeroPoint;
}
 
- (void)moveMouseToScreenPoint:(NSPoint)point
{
	CGPoint cgPoint = NSPointToCGPoint(point);
 
	CGSetLocalEventsSuppressionInterval(0.0);
	CGWarpMouseCursorPosition(cgPoint);
	CGSetLocalEventsSuppressionInterval(0.25);
}

App Store rank tracking that (hopefully) doesn’t suck

Published on 9/01/2012

I’ve recently become fairly obsessed with watching the ebb and flow of App Store rankings. This started recently, following the release of SpriteRight, but has since extended to encompass a number of unrelated applications.

Unfortunately I wasn’t able to find a simple, uncluttered web interface for viewing historic App Store ranking data. Which is why I spent this weekend knocking up AppStoreRank.net. It’s a fairly simple site with a fairly simple objective: display data and don’t piss people off in the process.

I use it to track my own applications and to also get an appreciation for just how turbulent ranking can be towards the lower end of the top 100 scale. Hopefully some of you find it interesting too.

Houdini to appear in the Mac App Store

Published on 29/12/2011

Houdini will soon appear in the Mac App Store. Relax! It’s still free. This is just a note to say that I will be retiring the currently active Sparkle appcast. Which means that all future updates will be exclusive to the App Store.

Edit: Houdini is now available to download.

SpriteRight

Published on 27/12/2011

Available now!

SpriteRight is a spritesheet generator for the Mac that lets you import your existing images or stylesheets. Make your sites load faster, cut bandwidth costs and save time. SpriteRight even generates CSS code on the fly. And best of all, it’s available now on the Mac App Store.

Net neutrality

Published on 11/10/2011

I’m fairly appalled by todays Orwellian announcement regarding Cameron’s push to negotiate pornographic filtering as an opt-out service across the UK’s internet service providers. This is, of course, a fully political move. It has very little to do with pornography and a whole lot to do with a government that is slowly but surely moving to violate net neutrality. They’ve chosen a contraband which, by its very nature, people are unlikely to protest. And they’ve done so under the guise of a moral obligation to young citizens.

The entire situation brings Eli Pariser’s talk on filter bubbles back into focus. It’s a wonderful presentation that really helps to personalise the net neutrality issue.