Hot damn! An implementation of Blocks for pre-10.6 operating systems, this time including installers, compiler support, and runtimes for both OS X 10.5 and iPhone OS 3.0.

I say again: Hot Damn!

The only downside I can see so far is that they’re not using github to host the source code :oP

For those with interest in helping out, there’s a forum set up at Google Groups for that express purpose.

11 hours ago on July 3rd, 2009 at 10:48 am | Permalink
This is a quick sample I rushed up using the only bit of non-icon-covered desktop space I had as a background. The button was originally designed to go on top of a fairly dark backround; I may have to tweak the colours a little to compensate.
This is a quick sample I rushed up using the only bit of non-icon-covered desktop space I had as a background. The button was originally designed to go on top of a fairly dark backround; I may have to tweak the colours a little to compensate.
1 day ago on July 2nd, 2009 at 6:58 pm | Permalink

AQGlassButton Internals

The AQGlassButton class is implemented using two CoreGraphics objects: a CGMutablePathRef and a CGGradientRef. The gradient defines the actual gloss appearance, while the path defines the shape of the button, and is used for both drawing its outline and for clipping the gradient when that is rendered.

Update: There’s a sample image of the button in its default setup in this followup post.

The Path

The path itself is a rounded rectangle. We create it in the -setup function, which is called any time the view’s frame is set. The path is drawn by creating a new CGMutablePathRef object and drawing lines and arcs, starting at the lower-left corner just above the arc. Note that cornerRadius is defined as 20% of the view’s current height, but this is just arbitrary based on my own personal tastes.


_path = CGPathCreateMutable();
CGPathMoveToPoint( _path, NULL, CGRectGetMinX(bounds), 
                   CGRectGetMinY(bounds) + cornerRadius );

We then draw the lines and the arcs which make up the shape of the button. First we draw a line up to height - cornerRadius, then we draw the arc. The arc is a bézier curve with control points at the top-left corner of the bounds rectangle.


CGPathAddLineToPoint( _path, NULL, CGRectGetMinX(bounds), 
                      CGRectGetMaxY(bounds) - cornerRadius );
CGPathAddArcToPoint( _path, NULL, CGRectGetMinX(bounds), CGRectGetMaxY(bounds),
                     CGRectGetMinX(bounds) + cornerRadius, 
                     CGRectGetMaxY(bounds), cornerRadius );

This repeats in similar fashion to draw the lines and arcs for top+right, right+bottom, and bottom+left. At the end we close the subpath; this might not be entirely necessary since we have ended at the same point at which we started, but I prefer to close the path explicitly for completeness.


CGPathCloseSubpath( _path );

The Gradient

The gradient is implemented using CGGradientRef. There are other ways (there are some nice third-party Objective-C gradient classes available, for example) but for simplicity I went with CoreGraphics’ version. Essentially the glass effect is created by using a gradient from the top to the bottom of the button with five colours defined at five points. The given tint colour (the default is {0.6, 0.6, 0.6}) is used as the basis of each colour, with its alpha channel pre-multiplied with our desired transparency values. This isn’t ideal, but it was the quickest way to get a form of tinting in place. Probably I should just full the button with the tint colour and overlay the gradient in the default grey; I’ll try that shortly.

To create the gradient we first need a colour space and an array of colours defined either as CGColorRef or individual CGFloat components. To save on unnecessary allocations I chose to use component colours. Since we’re basing this on the tint colour, we need to pull the existing components out of the supplied colour, and we will do the same with the colour space (this saves allocation and memory-management).


CGColorRef color = self.tintColor.CGColor;
CGColorSpaceRef space = CGColorGetColorSpace( color );

While fetching the components, we’ll fetch their number (RGB or HSB colors will have 4 components, Grayscale will have 2, and CYMK will have 5). We’ll also grab the color’s alpha value directly so we can pre-multiply our gloss transparency values.


size_t numComponents = CGColorGetNumberOfComponents( color );
const CGFloat * srcComponents = CGColorGetComponents( color );
CGFloat tintAlpha = CGColorGetAlpha( color );

To setup the gloss, we have to allocate the appropriate amount of memory to hold all our colour component values; we’ll need five entries of numComponents components, and we want some custom alpha channels.


CGFloat * components = (CGFloat *) NSZoneMalloc( [self zone], 
    numComponents * 5 * sizeof(CGFloat) );
    
int i, j;
CGFloat alphas[5] = {
    0.60 * tintAlpha,
    0.40 * tintAlpha,
    0.20 * tintAlpha,
    0.23 * tintAlpha,
    0.30 * tintAlpha
};
for ( i = 0; i < 5; i++ )
{
    for ( j = 0; j < numComponents-1; j++ )
        components[i*numComponents+j] = srcComponents[j];
    components[i*numComponents+j] = alphas[i];
}

The locations of each color are also specified in a plain C array, although this one doesn’t need to be dynamically allocated since there are a constant number of locations:


CGFloat locations[5] = {
    1.0, 0.5, 0.499, 0.1, 0.0
};

The locations are specified as a multiplicative function of the height of the rectangle in which the gradient is drawn; therefore 0.0 is where it starts and 1.0 is where it ends. Here we have it grading smoothly for half of the button’s height, then jumping to another color to smoothly grade up to the fourth, then a to the fifth color in the last 10% of the gradient. Note that we are supplying the locations in ‘reverse’ since this will actually draw the gradient bottom-up.

Lastly we create the gradient by handing all this information into the creation function:


_gradient = CGGradientCreateWithColorComponents( space,
    components, locations, 5 );
    NSZoneFree( [self zone], components );

Drawing the button

The button itself is drawn in (where else?) the -drawRect: method. Because we’ve done all the hard work elsewhere, saving our path and our gradient and doing all the appropriate math in advance, this is really quite simple.

First of all we set the stroke colour to a dark grey, and we clear the supplied rect:


[[UIColor darkGrayColor] setStroke];
    
[[UIColor clearColor] setFill];
UIRectFill( theRect );

We indicate a highlighted state by setting the fill colour to a very-nearly-transparent white:


if ( self.highlighted )
    [[UIColor colorWithWhite: 1.0 alpha: 0.1] setFill];

Now it’s down to the fiddly stuff. The path is used twice, for drawing the outline (with an optional fill) and for setting up the clipping path ready for the gradient to be drawn. Next we draw the gradient into the button’s bounds, with the clipping path causing it to fill our rounded-rectangle area only. The steps go like this:

  1. Get the current context.
  2. Begin a path in that context and add our saved path object.
  3. Draw it using the current stroke and fill colours (this clears the context’s current path in the process).
  4. Begin a new path in the context.
  5. Add our saved path object again.
  6. Clip the context to the current path (this also clears the current path).
  7. Draw the gradient in our bounds rectangle.

To draw the path with both a stroke and a fill, we use the kCGPathFillStroke constant as the options parameter to CGContextDrawPath(). Setting the clip rectangle to the current path involves a simple call to CGContextClip(). Drawing the gradient just requires passing a pair of points— the top and bottom of our bounds rectangle. The gradient simply takes this vector as its route, and it will fill the context’s clipping region across the perpendicular vector (i.e. since we supply a vertical vector, it will fill horizontally).

The code, which is substantially shorter than everything else, looks like this:


CGContextRef ctx = UIGraphicsGetCurrentContext();
    
CGContextSetLineWidth( ctx, 1.0 );
CGContextBeginPath( ctx );
CGContextAddPath( ctx, _path );
CGContextDrawPath( ctx, kCGPathFillStroke );
    
CGContextBeginPath( ctx );
CGContextAddPath( ctx, _path );
CGContextClip( ctx );
    
CGRect bounds = self.bounds;
CGContextDrawLinearGradient( ctx, _gradient,
    CGPointMake(bounds.origin.x, CGRectGetMaxY(bounds)),
    bounds.origin, 0 );

And there you have it! A simple-enough way to draw your own buttons in code, replete with teh-shiny, all without needing to think about images once.

1 day ago on July 2nd, 2009 at 4:37 pm | Permalink
An unfortunate brand name you have there…
An unfortunate brand name you have there…
1 day ago on July 2nd, 2009 at 2:40 pm | Permalink

This is a simple glass-effect UIButton subclass, implemented entirely using CoreGraphics. It’s probably not up to the sort of fidelity you can get with a stretched image (and a good illustrator), but it should serve for a nice introduction to the relevant techniques: paths, gradients, and colors.

**Update:** There’s an image of the default button on this followup post.

3 days ago on June 30th, 2009 at 9:25 pm | Permalink
Congratulations to my good friend David Kaneda, who has just got a place as speaker at this year&#8217;s 360|iDev in Denver.
Congratulations to my good friend David Kaneda, who has just got a place as speaker at this year’s 360|iDev in Denver.
4 days ago on June 29th, 2009 at 3:38 pm | Permalink | Reblog from

Windows → Mac File Transfers

Okay, not really a programming question, but I figure I know how to answer it (broadly at least) so why not?

I generally go for the path of least resistance. While it’s usually possible to burn data to a CD/DVD and use the good ol’ SneakerNet to move it across, Mac OS X supports Windows’ chosen network file-sharing protocol out of the box. You just need to see that it’s all set up properly.

Now, there are two ways of doing this:

  1. Create a shared folder on your Windows machine which you can access from your Mac to read from.
  2. Create a shared folder on your Mac which you can access from Windows to write to.

Since I don’t have a (working) Windows machine to hand to confirm setup instructions for option 1, I’ll go with option 2. There are a few relatively minor steps here:

  1. Create a folder somewhere. On your desktop is a nice enough place.
  2. Select the new folder and press ⌘-i, or choose ‘File → Get Info…’ from the menu bar.
  3. Check the ‘Shared folder’ option in the ‘General’ section of the inspector window. You can close it now.
  4. Open System Preferences and go into the Sharing pane.
  5. Select ‘File Sharing’ in the left-hand table. Ensure it’s checked (on).
  6. Click the ‘Options…’ button to the right.
  7. Check the ‘Share files and folders using SMB (Windows)’ checkbox, and check the box next to your username in the table below. You will be prompted to enter a password— this is the password you’ll use when accessing this share from Windows.
  8. Click ‘Show All’, and go into Network.
  9. Your current network interface should be selected — click ‘Advanced…’.
  10. On the ‘WINS’ tab of the sheet which appears, note the ‘NetBIOS Name’ value.
  11. Enter a value for ‘Workgroup’. A reasonable default is ‘workgroup’. This name, and the ‘NetBIOS Name’, are not case sensitive.
  12. Click ‘OK’ to close the sheet, then click ‘Apply’ at the bottom-right of the window.

At this point, you should be able to go to your Windows machine and go into ‘My Network’ (or is it called ‘Network Neighbourhood’, or something completely different now?) on your Start menu. Theoretically you should be able to see your Mac already, under the name you noted earlier. If not, use the navigation controls to go upwards and look for the workgroup called ‘workgroup’ (or whatever you chose to name yours). Go into that, and you should see your Mac. Double-click on it and you’ll see your share points. Double-click the one you want and you’ll be prompted for a username/password. Enter your Mac username and the password you entered in step 7 above. Et voilà. Drop files into that folder to put them on your Mac.

Now, this can be a little flaky. It might be worth rebooting both machines if it doesn’t appear to work. If the ‘My Network’ option isn’t available at all on your Windows machine you may need to install the ‘Client for Microsoft Networks’. I’ll leave those instructions to more experienced Windows people though.

4 days ago on June 29th, 2009 at 1:41 pm | Permalink
"No iPhone Flash is a GOOD THING. Don’t hate Apple for not supplying a plugin. Hate web developers who insist on using it."

Steve Streza on Twitter

The main issue seems to be that people using Adobe’s fairly decent implementation of Flash on Windows seem to assume that will carry over to the iPhone. However, the Mac version of Flash is notoriously slow, buggy, and prone to kill browsers. Not the sort of reputation Apple wants tarnishing their advanced mobile web experience.

4 days ago on June 29th, 2009 at 1:19 pm | Permalink
"It took me three days to figure out that there was another side to the tape."

BBC NEWS | UK | Magazine | Giving up my iPod for a Walkman

Okay, now I feel like a dinosaur (tip o’ the hat to John Gruber).

4 days ago on June 29th, 2009 at 1:06 pm | Permalink

Interesting Developments

So I should have something interesting and intriguing to exciting to announce tomorrow (or soon thereafter at least). In the meantime, I’m thinking about the sort of things I can write about here— specifically programming-related questions.

As I told an acquaintance recently: I manage to retain a silly amount of information, but without some sort of external impetus I often forget just what it is I know. In other words, I can probably answer a question about X, but I’m unlikely to find X should I just trawl my brain for interesting things about which to write.

So, I’d like to know if there’s anything anyone out there would like to know more about, or would like explained in a different way. Any pet peeves or seemingly intractable problems. Basically: any questions?

4 days ago on June 29th, 2009 at 12:53 pm | Permalink