Planet Cocoa

January 27, 2012

Gus Mueller

Hibari and JSTalk

Hibari is a Twitter client by Victoria Wang, and it supports my favorite scripting language, JSTalk:

[[JSTalk application:'Hibari'] tweet:'Hello World!'];
There are more examples in its advanced section of the user guide.

January 27, 2012 05:54 PM

Surfin' Safari

Vsevolod Vlasov is a WebKit Reviewer!

Vsevolod Vlasov has been a major contributor to the recent improvements to the Web Inspector. Please join me in congratulating Vsevolod on his new role as a WebKit reviewer!

by Pavel Feldman at January 27, 2012 01:08 PM

January 23, 2012

Gus Mueller

What Shipping Software and Newborns Have in Common

As we're raising Madeline, who is now a little over six weeks old, I'm finding that in a way it feels like I'm trying to ship some software. Frustration, happiness, OMG we're sooooo close to getting her to sleep past four hours (aka, we're soooooo close to making this usable).

But the most familiar feeling that I'm getting between the two experiences is this: each day is a step forward, and in the end work and patience is going to win the day.

January 23, 2012 08:34 PM

January 20, 2012

NSBlog

The Mac Toolbox: Followup

Welcome back to Friday, NSBlog readers. Here's the promised followup to my article last week about the Mac Toolbox, where I do a line-by-line analysis of the code from the original article, alongside Mike's Friday Q&A for this week.
(Read More)

January 20, 2012 04:14 PM

Friday Q&A 2012-01-20: Fork Safety

It's once again time to dive into bizarre programming arcana. In today's article, I want to look at the details of fork-safe code, why the restrictions are present, and why you might care, a topic suggested by Ben Mitchell.
(Read More)

January 20, 2012 02:07 PM

January 19, 2012

Gus Mueller

A Couple of Random Unrelated Thoughts on iBooks Author

Apple just came out with a neat app for the Mac today named iBooks Author. It does exactly what its title suggests, and that is to help you put together books for the iOS iBooks app. It's neat and it's free! And in a way, it feels like Keynote and Pages just had a kid.

A lot of people (including myself) were hoping Apple would come out with an ePub authoring tool today. The hope was there would finally be a usable tool for authoring and generating ePub files. Those ePub files could be uploaded to Apple's iBook Store, Amazon Kindle, or even your own store for sale. Or even make them downloadable off your website for free. Read them on your Kindle, iPad, iPhone, etc.


Random Thought #1:
I guess when I see it laid out like that, it's obvious why iBooks Author has the restrictions it does. Why make a tool which could then be used to make great products to be used on competitor's platforms?


Random Thought #2:
Apple releasing iBooks Author makes it easier for indie devs to jump into the arena now. Apple has shown us what their vision is, and if you don't like it- tough. You can use another app.

And that means indie devs can now make that app without being worried that Apple will pull the rug out from under them. (And believe me- the indie guys who would like to do this frequently worry about such things).


Random Thought #3:
It's not scriptable in any meaningful way. If you want to drive it with AppleScript to make your iBook, you're out of luck.


Bonus Crazy Thought:
I really hope Xcode doesn't ship with the same restrictions some day. "Binaries created through Xcode can only be sold through the App Store, and you can't charge more than $15.99".

I don't see it happening, but at the same time such a move wouldn't surprise me. They wrote Xcode after all, it's free, and Apple can do what it wants with it. Go somewhere else if you don't agree.

January 19, 2012 10:33 PM

January 18, 2012

Clickable Bliss

Making a better alert prompt

One of my current projects in an event management system that generates mobile apps for the attendees of the event. Can’t wait to talk more about this project but that’s it in a nutshell.

In our iOS version we have a bookmark system. When a users goes to bookmark an event we present a dialogue asking if they’d like to enable a feature that would “mirror” their bookmarks onto the system’s calendar.

Question: Is this a good practice? Its seems like too aggressive of a feature to leave on by default but considering our users won’t have a long relationship with the app it’d be rare they discover this on their own. I’ve suggested we try to track usage of this feature so we can make a more informed default later and maybe drop the dialogue entirely. Thoughts?

The alert we currently show in beta is pretty bad.

alert we currently show

**Save to calendar?**
Would you like to automatically  
save bookmarks to your Calendar  
app? (You can change this  
preference later in your device  
settings)
[Save to Calendar] [No]

Things I don’t like about this:

  • It’s wordy.
  • The string “Save to Calendar” is so long it makes the alert view use a different font size to fit it.
  • The use of “No” is not a natural opposite to “Save…”. In general I don’t like YES/NO alerts as they require you to read the alert message. By using action words like SAVE, OPEN, CLOSE, LINK you have a better guess at what’s going to happen when you tap the button.
  • Capitalization of “Calendar” isn’t consistent.
  • The sentence inside of the parentheses doesn’t have a period.
  • Using terms like “your Calendar app” are excessive for such a personal device. Of course it’s your Calendar. This isn’t a multi-user system.

A new alternative:

**Sync Bookmarks to Calendar?**
Syncing will show all the events
you bookmark in the Calendar app.
[Sync] [Cancel]

I like the word sync better than save since we do in fact keep those calendar listings up to date with changes that come down the wire. I feel like dropping the whole “this can be changed later in preferences” message helps the alert feel less intimidating. The setting panel itself is actually a high level menu in our navigation controller. It’s not too hard to find if you go looking for it and this Calendar feature is the first setting listed.

Question: What do you think of my new alert dialogue? Have any further suggestions?

While it can sometimes feel excessive to analyze single alerts like this I do feel that small improvements do add up to make the overall app experience better. Hopefully you enjoyed a little insight into my own UI refactoring workflow.

by Mike Zornek at January 18, 2012 08:06 PM

NSBlog

Plausible Labs is Hiring

Plausible Labs, creators of fine software and employer of me, is looking for a software engineer in New York City or San Francisco. Do you like to create fine software? Do you want to join a worker-owned cooperative filled with great people? Do you want to work with a guy who writes a blog? If so, check out our announcement.
(Read More)

January 18, 2012 03:38 PM

January 17, 2012

Red Sweater Blog

Shush 2.0

By now I am somewhat famous for my slowness in adopting the iOS platforms for my major apps: MarsEdit and Black Ink. The truth is I have been working on these releases for years, but it’s also true that work has been intermittent as I refocus on Mac versions of my apps and on other commitments in my life.

One of those other commitments has been raising a small family. My son, Henry, will become a big-brother in a matter of hours or days, and in honor of that I’ve decided to update my only shipping iOS app: Shush.

Shush 1.0 was a simple, dare I say embarrassingly simple, project that came out of our interest as new parents in Harvey Karp’s Happiest Baby on the Block techniques for soothing infants in the first few months of life. Among the bag of tricks is shushing the baby, creating white noise with your mouth: “Shhh.” While this trick worked for us, it became a little exhausting to make the noise for as long as it seemed helpful to Baby Henry.

For the new baby, I anticipate using Shush again, so I decided to give it a facelift. I had imagined over the years since I first released it that it would be fun to have it embrace some of the iPhone’s playfulness and provide a highly skeuomorphic television-set style design. This is Shush 2.0:

ShushScreenshotWithDevice

Don’t get me wrong: I know I won’t win any graphic-design awards for this, although it represents a peak of my skills in that area. Those of you who remember Shush 1.0 will probably consider this at least slightly more visually appealing. I tried to maintain the simplicity of Shush 1.0 while livening up the interface. I actually simplified a bit by removing the “Start/Stop” button. To turn Shush 2.0 off, you just slide the volume to its lowest position. To make the TV metaphor work, Shush is locked to landscape orientation. But I positioned the slider so it would be easily and intuitively navigated with the thumb while “holding the phone wrong” in an upright position.

Also new in this release are Shush’s ability to make static noise in the background while you continue to use your iPhone or iPad. I imagine this will be handy especially for parents who want to produce that sweet, soothing static, but would also like to so catch up on Instapaper, Twitter, or whatever while they’re cradling the baby.

Finally, that skeuomorphic television static actually provides something of a hypnotic animated effect. Some users may find the visual display useful either for lulling themselves or for distracting and amusing a baby. From a technical standpoint I’m particularly proud of the effect. Inspired by a suggestion from Mike Ash, I implemented the static animation as an OpenGL Shader, so it runs almost entirely on the iPhone’s GPU. This means it is extremely efficient and not liable to slow down your phone or gobble up your battery. If you don’t like the TV static or just want to save even more power while Shushing, you can put the display to sleep and Shush keeps on Shushing.

I hope you enjoy Shush 2.0. Let me know if you give it a try or have feedback about my decisions in redesigning this simple application. To answer the inevitable question: yes, MarsEdit and Black Ink are still under development for iOS!

by Daniel Jalkut at January 17, 2012 05:42 PM

January 13, 2012

NSBlog

Friday Q&A 2012-01-13: The Mac Toolbox

Hi again, readers of NSBlog! This is my first guest post of 2012, and also my first one writing the introduction myself. I plan to do posts every two weeks, alternating with Mike's to bring back the once-a-week format. This week, at my own suggestion, I'm doing a historical piece on the original programming environment for Macs: The Mac Toolbox. Time for a trip down memory lane!
(Read More)

January 13, 2012 07:28 PM

January 11, 2012

Cocoa Is My Girlfriend

Handling incoming JSON redux

A few months ago I wrote here about a generic approach to safely take incoming JSON and save values to Core Data object. The goals of that code were twofold:

  1. Provide a safe, generic alternative to Cocoa’s -setValuesForKeysWithDictionary: for use with NSManagedObject and its subclasses
  2. Handle cases where JSON data didn’t match up with what the managed objects expected. Getting a string where you expect a numeric value, or vice versa, for example, or getting a string representation of a date when you want a real NSDate object.

The first item was the most important. It’s tempting to use -setValuesForKeysWithDictionary: to transfer JSON to a model object in one step. The method runs through the dictionary and calls -setValue:forKey: on the target object for every entry. It has a fatal flaw though, in that it doesn’t check to see if the target object actually has a key before trying to set it. Using this method when you don’t have absolute control over the dictionary contents is an invitation to unknown key exceptions and nasty app crashes.

Fixing this for managed objects was relatively easy because Core Data provides convenient Objective-C introspection methods. The general approach was:

  • Get a list of the target object’s attributes
  • For each attribute, see if the incoming dictionary has an entry. If so,
    • Compare the incoming type to the expected type, and convert if necessary.
    • Call -setValue:forKey: with that key and its value.

And then just last week I had the thought, wouldn’t it be nice if this worked for any object, not just for managed objects?

Objective-C introspection

Since Objective-C is dynamic, pretty much everything you’d want to know about a class is available at run time. I’m not just talking about methods like -respondsToSelector: and -isKindOfClass:, though those are extremely useful. You can go much, much deeper than that, inspecting (and even changing) every aspect of a class’s implementation. Much of this happens via C function calls rather than Objective-C method calls. The Objective-C runtime is not actually written in Objective-C, and it’s the runtime that has the information.

To update the code for use with objects that don’t inherit from NSManagedObject the new code looks through the properties declared on a class and uses those to run through the incoming JSON. The general approach is the same but the implementation uses NSObject properties instead of NSEntityDescription attributes.

It’s also possible to look through the instance variables instead of the properties. Often this would amount to the same thing. Where they differ is when the backing ivar has a different name, i.e. when you’re using something like:

@synthesize foo = __myReallyBizarrePrivateName____;

In that case iterating over properties would find foo while iterating over instance variables would find __myReallyBizarrePrivateName____. Either is valid but I’m going with the properties because (at least for me) they’re more likely to match up with the JSON keys.

First pass: iterating over properties

A simple version that meets requirement #1 looks like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
- (void)setValuesForKeysWithJSONDictionary:(NSDictionary *)keyedValues dateFormatter:(NSDateFormatter *)dateFormatter
{
    unsigned int propertyCount;
    objc_property_t *properties = class_copyPropertyList([self class], &propertyCount);
 
    for (int i=0; i<propertyCount; i++) {
        objc_property_t property = properties[i];
        const char *propertyName = property_getName(property);
        NSString *keyName = [NSString stringWithUTF8String:propertyName];
 
        id value = [keyedValues objectForKey:keyName];
        if (value != nil) {
            [self setValue:value forKey:keyName];
        }
    }
    free(properties);
}

This starts with a call to class_copyPropertyList(), which gets a C-style array of property declarations for the requested class. The propertyCount argument indicates how many properties are in the array. The array contains zero or more objc_property_t entries, which is an opaque structure.

The code iterates through this array. For each one it uses property_getName to get the property name as a C-style string. Then it converts this to an NSString and uses that to look up entries in the incoming dictionary. And, voila, we’re using the class’s own properties to look up values in the dictionary instead of the other way around.

A final detail– unusual in Objective-C code– is the call to free(). Since class_copyPropertyList() has copy in its name, the calling code is responsible for disposing of the returned data. And since it’s a C call, this needs to be done C style. This call would need to be there even if the project were using ARC.

Back to Core Data, briefly

The great thing about this solution is that it’s not the non-managed-object alternative to the previous version, it’s a direct replacement. This approach works just as well on managed objects as on other objects– provided, that is, that you create custom subclasses of NSManagedObject for your entities that declare properties for managed object attributes. So long as the properties exist, the code works. If you’re using Xcode or mogenerator to generate your managed object subclasses, you’re covered. If you aren’t creating custom subclasses, first of all, why not? But in that case this approach won’t work since NSManagedObject doesn’t have the necessary property declarations.

Does it have to be like this?

Some of you may have noticed that it’s possible to do the same thing without any mucking about with the runtime by doing something like this:

1
2
3
4
5
6
7
8
    for (NSString *key in keyedValues) {
        @try {
            [self setValue:[keyedValues objectForKey:key] forKey:key];
        }
        @catch (NSException *exception) {
            // Do nothing
        }
    }

In this case the dictionary keys still drive the action, but exception handling means the code doesn’t crash on unknown keys. So why bother then? Because of requirement #2 above. Coercing JSON into appropriate data types is going to require introspection. With this simplified approach you don’t crash, but you also don’t get type conversions. If not crashing is all you’re interested in, this works just as well and is probably faster. It’s certainly simpler anyway. It’s not going to do what I need though.

JSON Fixes

To meet requirement #2 the code needs to go deeper. As with the Core Data implementation, it needs to look up the expected value for the property and compare that to the type of the incoming data. To do this I’ll expand the if block beginning on line 12 above to look like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
if (value != nil) {
    char *typeEncoding = NULL;
    typeEncoding = property_copyAttributeValue(property, "T");
 
    if (typeEncoding == NULL) {
        continue;
    }
    switch (typeEncoding[0]) {
        case '@':
        {
            // Object
            Class class = nil;
            if (strlen(typeEncoding) >= 3) {
                char *className = strndup(typeEncoding+2, strlen(typeEncoding)-3);
                class = NSClassFromString([NSString stringWithUTF8String:className]);
            }
            // Check for type mismatch, attempt to compensate
            if ([class isSubclassOfClass:[NSString class]] && [value isKindOfClass:[NSNumber class]]) {
                value = [value stringValue];
            } else if ([class isSubclassOfClass:[NSNumber class]] && [value isKindOfClass:[NSString class]]) {
                // If the ivar is an NSNumber we really can't tell if it's intended as an integer, float, etc.
                NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
                [numberFormatter setNumberStyle:NSNumberFormatterDecimalStyle];
                value = [numberFormatter numberFromString:value];
                [numberFormatter release];
            } else if ([class isSubclassOfClass:[NSDate class]] && [value isKindOfClass:[NSString class]] && (dateFormatter != nil)) {
                value = [dateFormatter dateFromString:value];
            }
 
            break;
        }
 
        default:
        {
            break;
        }
    }
    [self setValue:value forKey:keyName];
    free(typeEncoding);
}

The runtime provides information about properties via the call to property_copyAttributeValue() on line 3. The first argument is the property of interest. The second one can have a bunch of different values depending on what you want to look up. A “T” requests a C-style string that describes the property type. For full details on what you can do with the second argument, see the (somewhat out of date as of this writing) “Declared Properties” section of Apple’s Objective-C Runtime Programming Guide.

If the property type is a pointer to a class, the return value will be something like T@"NSNumber", T@"NSString", T@"MyClass", etc. The next thing the code does then is to check for a leading @ and, if found, set about getting the Class that the type string names. This happens in lines 12-16. The code strips off the @ and the quotes, converts to NSString, and uses NSClassFromString to get the Class.

The rest of this code is remarkably similar to the Core Data version, looking for type mismatches and converting the incoming value where needed. The chief difference is that it uses -isSubclassOfClass: to check on the expected type of the property instead of looking at the Core Data-specific NSAttributeType.

Except for one important difference. For numeric properties, the Core Data attribute type would indicate whether a floating point or integer value was expected. With NSNumber we have no way of knowing. So the code uses NSNumberFormatter to parse incoming strings and leaves it at that. If this kind of mismatch occurs then there are bigger problems anyway. You could change the code to round a float to an int, but is that actually going to be a valid result? Maybe, maybe not.

The type comparisons in this code could go on forever but in this case the code is specifically looking for problems that sometimes crop up with JSON.

Since property_copyAttributeValue() has copy in its name, this block adds another call to free() to clean up after itself.

Handling primitives

But what if the expected value is not an object at all? What if it’s a primitive int? Fortunately property_copyAttributeValue() covers that case as well. In this case the type encoding string is shorter, with values like Ti for int, Tf for float, TQ for unsigned long long, etc (again, see Apple’s docs for a full list).

With this information, the switch statement above can be expanded with a few more cases.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
        case 'i': // int
        case 's': // short
        case 'l': // long
        case 'q': // long long
        case 'I': // unsigned int
        case 'S': // unsigned short
        case 'L': // unsigned long
        case 'Q': // unsigned long long
        case 'f': // float
        case 'd': // double
        case 'B': // BOOL
        {
            if ([value isKindOfClass:[NSString class]]) {
                NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
                [numberFormatter setNumberStyle:NSNumberFormatterDecimalStyle];
                value = [numberFormatter numberFromString:value];
                [numberFormatter release];
            }
            break;
        }
 
        case 'c': // char
        case 'C': // unsigned char
        {
            if ([value isKindOfClass:[NSString class]]) {
                char firstCharacter = [value characterAtIndex:0];
                value = [NSNumber numberWithChar:firstCharacter];
            }
            break;
        }

The first half of this, up to line 20, handles all the numeric types. As with the previous code block there’s no attempt to work out floating point/integer conflicts since it’s impossible to know how to handle this in the general case. It would be possible to break this up into more specific checks, say by using -[NSString intValue] when an integer result is expected and hope for valid data. But the code above handles the case where you actually have an integer value, and if you don’t have one then again, you have bigger problems.

The rest of this block handles a primitive char property by taking the first character in the incoming string. Longer strings can’t be stored in a char anyway, so this is the best approach.

In both cases the conversion results in an NSNumber instead of a primitive type. That’s OK though– -setValue:forKey: has our back here and will unbox the object for us.

Conclusion

Using this approach makes it a lot easier to deal with web services. You can’t always rely on the results matching what you expect (or what’s documented). Inspecting classes at run time takes a more defensive approach to dealing with data you can’t control.

A category on NSObject that implements this code can be found at github.

by Tom Harrington at January 11, 2012 03:00 PM

January 09, 2012

NSBlog

Buy Videos of My VTM Presentations

I gave two presentations at the Voices That Matter conference in Boston in November. Both presentations were recorded and the videos are now available for purchase. And as a special present for my readers, you can get 35% off!
(Read More)

January 09, 2012 04:52 PM

January 06, 2012

NSBlog

Friday Q&amp;A 2012-01-06: The Hopper Disassembler

We've spent the last few weeks talking about disassembling executables and how to read the result. Today, I want to wrap up that discussion with a look at a powerful third-party disassembly tool called Hopper.
(Read More)

January 06, 2012 03:48 PM

January 05, 2012

Safe from the Losing Fight

New Product Preview, Kind of

I’ve been hard at work on Fortunate Bear’s next product, but I wanted to lift my head long enough to give a quick preview of what I’m working on. Keep in mind, it’s very early in development and maybe 20% (if that) of the features are done. But without further ado:



(Video is HTML 5, so you’ll need a recent browser.)

As a bonus you get to see my fantastical art skills. The codename for the project is Vectorspring, and I hope to be showing off more as it progresses.

by andy at January 05, 2012 10:13 PM

January 04, 2012

Gus Mueller

Running a website with VoodooPad Pro

Brett Terpstra: Running a website with VoodooPad Pro.

"VoodooPad is a powerful note-collecting, wikifying, productivity machine. With the right scripts, you can make just about anything happen, at least within the realm of text, images and web pages. VoodooPad has a built in web server, and can export your notebook as a full website, with links intact. I like the web export the best, because it gives me a crazy amount of control over the output. To that end, I built a website using nothing but VoodooPad, hopped up on some custom scripts."

Brett shows how to combine Python (for multi markdown support), VP's Event Scripts (to make a site map), a custom web export template, and post processing scripts to create the exact website he wants.

January 04, 2012 09:35 PM

January 02, 2012

Red Sweater Blog

Learn To Code

If learning to program is even a minor goal for you, Code Year (via Brent Simmons) might be just the encouragement you need. They promise to email you on a weekly basis with coding lessons to help you achieve your goal.

I’m one of those computer programmers who downplays the difficulty of the profession, because “if I can do it, anybody can do it!” On the other hand, I have faced challenges that made me question whether I’m vaguely qualified for the job. What it boils down to is that programming is both incredibly simple and impossibly hard, like so many important things in life.

There was a time when nobody knew how to write literary prose. The geniuses who invented it shared their special tool with a few friends, and they relished in their private, elite communications. Eventually monks, politicians, and academics joined the club. Now, we judge a society’s overall level of intellectual advancement by the literacy rate: the percentage of people who have learned to read and write.

Literacy isn’t about becoming a Hemingway or a Chabon. It’s about learning the basic tools to get a job done. I think programming — coding — is much the same. You don’t have to be the world’s best programmer to develop a means of expressing yourself, of solving a problem, of making something happen. If you’re lucky, you’ll be a  genius, but you start out with the basics.

Long ago, it would have been ridiculous to assume a whole society could be judged by its ability to read and write prose. It feels ridiculous now, to assume that we might use computer programming as a similar benchmark. Yet it may happen.

Did you always mean to learn another language, but never did? By all means, learn Spanish, French, or Chinese. But learn to code, too.

by Daniel Jalkut at January 02, 2012 04:20 AM

January 01, 2012

Cocoa With Love

Advanced drawing using AppKit

In this post, I'll look at drawing a detailed image in code by combining multiple visual elements. Unlike previous posts I've done on drawing in Cocoa, this will focus on the AppKit classes. The code will use NSGraphicsContext, NSBezierPath, NSAffineTransform, NSGradient, NSGlyph and show you some simple ways to export the contents of an NSView to a file.

Introduction

I was reading an article on the Mac App Store and instead of paying attention to the article or working (which is what I was supposed to be doing at the time) I found myself staring at the accompanying image (a large version of the Mac App Store icon). Being the Cocoa programmer that I am, I found myself mentally deconstructing the icon and wondering how you'd draw something similar in Cocoa.

Only after I'd written the code and decided to write a post on it did I pay attention to the fact that I've already written a series of posts on creating icons. I'm not trying to be repetitive (this post is about Cocoa drawing techniques; it is not a suggested way to create application icons as the previous post was) but now that it's too late to change, I wish I had chosen a more novel visual subject for this post.

Update 2011-05-28: this post is a Mac application. If you'd like to see the same design drawn using iOS CoreGraphics code, check out this blog post by Marcus Crafter.

The sample app

iconapp.png
You can download the complete sample project used in this post here IconApp.zip (96kB)

The application has a single window into which it draws an icon. You can resize the window and the icon always scales to fit. You can export the icon to a PDF or PNG file.

Obviously, while inspired by the Mac App Store icon, this design is not trying to accurately replicate it. The real purpose is to show how to achieve the multiple, layered gradients, curve drawing, path-based clipping, shadowing and scaling you're likely to need if you want to create a non-trivial design in code.

Scale-to-fit (respecting aspect ratio) and center

Even before we start drawing, we need to ensure that the image will always be scaled to fit the window. The first step is to find a scale that will fit the square icon into the view regardless of view size or aspect ratio:

NSSize nativeSize = [self nativeRect].size;
NSSize boundsSize = self.bounds.size;
CGFloat nativeAspect = nativeSize.width / nativeSize.height;
CGFloat boundsAspect = boundsSize.width / boundsSize.height;
CGFloat scale = nativeAspect > boundsAspect ?
    boundsSize.width / nativeSize.width :
    boundsSize.height / nativeSize.height;

In this case, the nativeRect of the view is defined as NSMakeRect(0, 0, 512, 512);

Once we have the desired scale factor, we resize the current drawing context and center it:

NSAffineTransform *transform = [[NSAffineTransform alloc] init];
[transform
    translateXBy:0.5 * (boundsSize.width - scale * nativeSize.width)
    yBy:0.5 * (boundsSize.height - scale * nativeSize.height)];
[transform scaleBy:scale];
[transform set];

Once we've applied this affine transform, we can draw as though our canvas is the nativeRect size but it will perform an aspect fit for any view size.

I haven't shown it here but if you change the current transform (or the current clipping path as I also do later) you should remember to place a call to [[NSGraphicsContext currentContext] saveGraphicsState] before your changes and follow your drawing by a call to [[NSGraphicsContext currentContext] restoreGraphicsState] to put everything back again.

Draw the background and shadow

iconapp-boundary.png

We draw the background circle initially using a flat color, with an NSShadow enabled.

We use a flat color while drawing the shadow (instead of the gradient that we want in the final output) because the gradient drawing method we're going to use actually clips to the gradient's boundary, so the shadow wouldn't be drawn (because the shadow would be outside the clipping boundary).

Also, gradients don't anti-alias their edges. Drawing the shape with a flat color that is approximately the average color of the gradient will give us a nice, anti-aliased edge.

[NSShadow setShadowWithOffset:NSMakeSize(0, -8 * scale) blurRadius:12 * scale
    color:[NSColor colorWithCalibratedWhite:0 alpha:0.75]];
[[NSColor colorWithCalibratedWhite:0.9 alpha:1.0] set];
[[NSBezierPath bezierPathWithOvalInRect:ellipseRect] fill];
[NSShadow clearShadow];
Coding practice aside: If you're a good coder, you should avoid "magic numbers". Magic numbers are unnamed numbers used without explanation in the code. They are considered bad practice (instead, you should assign values to a constant that names the value and explains any derivation, then you may use it).

However, I normally make an exception for drawing code (as I have in this case). If a number is chosen purely for aesthetic purposes and has no real relationship to any other value, I leave it magic on the assumption that its aesthetic purpose (with derivation or geometric relationship) is obvious.

You do need to keep a tight reign on permissive number usage though. You'll notice lower down in this post (in the "Gloss Gradient" code), where there's a blend of calculated, derived and relationship-related values as well as purely aesthetic values, I've bothered to use named constants instead, to clarify what is derived, what is proportionate and which arc is which.

The shadow methods in the previous code block are convenience methods from a category implemented as follows:

@implementation NSShadow (SingleLineShadows)

+ (void)setShadowWithOffset:(NSSize)offset blurRadius:(CGFloat)radius
    color:(NSColor *)shadowColor
{
    NSShadow *aShadow = [[[self alloc] init] autorelease];
    [aShadow setShadowOffset:offset];
    [aShadow setShadowBlurRadius:radius];
    [aShadow setShadowColor:shadowColor];
    [aShadow set];
}

+ (void)clearShadow
{
    NSShadow *aShadow = [[[self alloc] init] autorelease];
    [aShadow set];
}

@end

We then draw the gradient over the top:

NSBezierPath *ellipse = [NSBezierPath bezierPathWithOvalInRect:ellipseRect];
NSGradient *borderGradient =
    [[[NSGradient alloc]
        initWithStartingColor:[NSColor colorWithCalibratedWhite:1.0 alpha:1.0]
        endingColor:[NSColor colorWithCalibratedWhite:0.82 alpha:1.0]]
    autorelease];
[borderGradient drawInBezierPath:ellipse angle:-90];

Layered gradients to form the background

Single, simple gradients tend to look bland and artificial. Our eyes expect much more complex lighting than a single gradient normally provides.

For this gradient, I wanted a pear shape as though a circular gradient had been smudged upwards. NSGradient does have methods that allow the gradient to be drawn as a circle along a path but the aesthetic is harsher than the soft, blurry look I wanted.

The easiest way to get a natural, soft-lighting style gradient effect is simply to lay a few gradients on top of each other. By getting the alpha transparency of each gradient right, the gradients will all appear to be part of the same effect. Of course, this is also slow, particularly at high resolutions, so it is an approach to use sparingly.

iconapp-gradients.png

The final result in the bottom right corner of this image is achieved by drawing a black background, then drawing the top-left, top-right and bottom-left gradients over the top of the black background.

Here's the code to paint the center of the circle black. We then set the clipping path of the context so that all further drawing will be clipped to inside the border of the circle.

NSRect ellipseCenterRect = NSInsetRect(ellipseRect, 16, 16);
[[NSColor blackColor] set];
NSBezierPath *ellipseCenter = [NSBezierPath bezierPathWithOvalInRect:ellipseCenterRect];
[ellipseCenter fill];

[ellipseCenter setClip];

Now that the code is complete, I realize that I didn't need to clip here. I could have used the -[NSBezierPath drawInBezierPath:relativeCenterPosition:] method for the gradients to automatically draw them clipped to the ellipseCenter path and they would have clipped themselves (nothing else would have needed clipping). Performance-wise, it doesn't really matter though (either I apply the clip or the method does)..

Once the background is drawn, we begin layering the gradients. This is the code that draws the top-left of the gradients shown above:

NSGradient *bottomGlowGradient =
    [[[NSGradient alloc]
        initWithColorsAndLocations:
            [NSColor colorWithCalibratedRed:0 green:0.94 blue:0.82 alpha:1.0], 0.0,
            [NSColor colorWithCalibratedRed:0 green:0.62 blue:0.56 alpha:1.0], 0.35,
            [NSColor colorWithCalibratedRed:0 green:0.05 blue:0.35 alpha:1.0], 0.6,
            [NSColor colorWithCalibratedRed:0 green:0.0 blue:0.0 alpha:1.0], 0.7,
        nil]
    autorelease];
[bottomGlowGradient
    drawInRect:ellipseCenterRect relativeCenterPosition:NSMakePoint(0, -0.2)];

The floral heart adornment

iconapp-floralheart.png

The floral heart is a character from the Arial Unicode MS font. Getting the bezier path of a font character is a little cumbersome, since you need to use NSLayoutManager to get the glyph for you before you can ask NSBezierPath to create a path from the glyph.

NSString *floralHeart = @"\u2766";
NSRange stringRange = NSMakeRange(0, [floralHeart length]);
NSFont *arialUnicode =
    [[NSFontManager sharedFontManager]
        fontWithFamily:@"Arial Unicode MS"
        traits:0
        weight:5
        size:345];
NSLayoutManager *layoutManager = [[[NSLayoutManager alloc] init] autorelease];
NSTextStorage *textStorage =
    [[[NSTextStorage alloc] initWithString:floralHeart] autorelease];
[textStorage addAttribute:NSFontAttributeName value:arialUnicode range:stringRange];
[textStorage fixAttributesInRange:stringRange];
[textStorage addLayoutManager:layoutManager];
NSInteger numGlyphs = [layoutManager numberOfGlyphs];
NSGlyph *glyphs = (NSGlyph *)malloc(sizeof(NSGlyph) * (numGlyphs + 1)); // includes space for NULL terminator
[layoutManager getGlyphs:glyphs range:NSMakeRange(0, numGlyphs)];
[textStorage removeLayoutManager:layoutManager];

NSBezierPath *floralHeartPath = [[[NSBezierPath alloc] init] autorelease];
[floralHeartPath moveToPoint:NSMakePoint(130, 140)];
[floralHeartPath appendBezierPathWithGlyphs:glyphs count:numGlyphs inFont:arialUnicode];
free(glyphs);

If there's an easier way to do this, I'd love to know. It certainly seems like you should just be able to ask for the path for a Unicode character directly.

The floral heart is then drawn using a gradient and shadow in almost the same way as the frame (we even use the same gradient).

Update: As Christopher Lloyd points out in the comments, you can get the NSGlyph more easily using the CTFont function CTFontGetGlyphsForCharacters. It saves 9 lines of code but it breaks my effort to do everything in this post using AppKit instead of the Core APIs:

// Replaces lines 1, 2, 9-18 of previous code block
NSInteger numGlyphs = 1; // hard-coded glyph count for floral heart character
NSGlyph *glyphs = (NSGlyph *)malloc(sizeof(NSGlyph) * (numGlyphs + 1)); // includes space for NULL terminator
CTFontGetGlyphsForCharacters(
    (CTFontRef)arialUnicode, (const UniChar *)L"\u2766", (CGGlyph *)glyphs, numGlyphs);

Gloss gradient

iconapp.png

The icon is finished off with a gloss gradient. The gloss gradient is drawn between two arcs and it is these arcs that are the tricky part.

The gloss itself is inset from the frame to ensure that between the nearly white color of the frame and the gloss gradient, we still get a dark rim for contrast.

The left and right edges of the gloss start just above the middle of the circle (I chose 0.02π radians), following the arc of the circle at the top and through the middle of the image it bulges down to approximately the middle of the circle.

I got the trignometry for the top arc correct but I didn't really bother with the bottom bulging arc (since its exact placement doesn't really matter. Due to the nature of the 3 point arc drawing method I used for this bottom arc, if you get the radius wrong for the three point locations, the arc either straightens out near the left and right endpoints or the arc overshoots these points. I'm sure I could have done this better (a bézier curve might have been a smarter choice since a proper circle was not required for this curve).

const CGFloat glossInset = 8;
CGFloat glossRadius = (ellipseCenterRect.size.width * 0.5) - glossInset;
NSPoint center = NSMakePoint(NSMidX(ellipseRect), NSMidY(ellipseRect));

double arcFraction = 0.02;
NSPoint arcStartPoint = NSMakePoint(
    center.x - glossRadius * cos(arcFraction * M_PI),
    center.y + glossRadius * sin(arcFraction * M_PI));
NSPoint arcEndPoint = NSMakePoint(
    center.x + glossRadius * cos(arcFraction * M_PI),
    center.y + glossRadius * sin(arcFraction * M_PI));

NSBezierPath *glossPath = [[[NSBezierPath alloc] init] autorelease];
[glossPath moveToPoint:arcStartPoint];
[glossPath
    appendBezierPathWithArcWithCenter:center
    radius:glossRadius
    startAngle:arcFraction * 180
    endAngle:(1.0 - arcFraction) * 180];

const CGFloat bottomArcBulgeDistance = 70;
const CGFloat bottomArcRadius = 2.6;
[glossPath moveToPoint:arcEndPoint];
[glossPath
    appendBezierPathWithArcFromPoint:
        NSMakePoint(center.x, center.y - bottomArcBulgeDistance)
    toPoint:arcStartPoint
    radius:glossRadius * bottomArcRadius];
[glossPath lineToPoint:arcStartPoint];

Exporting

Exporting a view to a PDF file is extremely simple in Cocoa:

[[iconView dataWithPDFInsideRect:[iconView nativeRect]]
    writeToURL:[savePanel URL]
    atomically:YES];

However, you'll quickly see that gradients with transparency don't output correctly in Mac OS X (everything else, including the shadows, work fine). The internets claim it's a limitation of PDF 1.4 (the PDF standard used in Mac OS X).

Instead we need to export to PNG if we want to keep the transparency. There's a few different ways to do this but the quickest and easiest is:

NSRect iconViewFrame = iconView.frame;
[iconView setFrame:[iconView nativeRect]];

NSBitmapImageRep *bitmapImageRep =
    [iconView bitmapImageRepForCachingDisplayInRect:[iconView frame]];
[iconView
    cacheDisplayInRect:[iconView bounds]
    toBitmapImageRep:bitmapImageRep];
[[bitmapImageRep representationUsingType:NSPNGFileType properties:nil]
    writeToURL:[savePanel URL]
    atomically:YES];

[iconView setFrame:iconViewFrame];

This approach contains a few potential problems:

  • we need to resize the view to render at the desired "native" resolution
  • it caches the screen render so it may contain screen-related oddities (like your screen's color profile)

Ultimately, creating your own NSBitmapImageRep, setting the NSGraphicsContext using graphicsContextWithBitmapImageRep:, locking focus yourself and invoking drawRect: directly will avoid these issues and is more flexible. But it would have been more work so I didn't bother for this sample project (I mostly put this export code into the app so I could create the app's icon).

Conclusion

You can download the complete sample project used in this post here IconApp.zip (96kB)

Ultimately, it's uncommon to construct this many visual elements in code; these things are invariably easier in a drawing program and a prerendered bitmap would be faster (this is quite slow at large sizes).

However there are many situations where drawing in code can offer advantages — particularly if your designs need to reshape or adapt to fit content. Buttons and other controls containing text are a an example, particularly if your design cannot simply be stretched.

Even if the most complicated thing you need to do is anti-alias a path filled by a gradient, there are a few tricks contained in this post that I hope will help.

by Matt Gallagher (noreply@blogger.com) at January 01, 2012 03:58 PM

NSBlog

Avoid Apress

Nearly three years ago, I got involved with Apress to contribute to two books. The experience went disastrously wrong. It was a profoundly unpleasant experience and I want to warn any other prospective authors to avoid this publisher, and encourage readers to buy from other publishers. Warning: this post is non-technical and something of a rant. If that's not your thing, please skip it and come back in a week when technical goodies shall resume their normal course.
(Read More)

January 01, 2012 03:27 AM

December 31, 2011

briksoftware Blog

New Tool: CertificateHelper

Last fall I got a new iMac that I only use during the Weekends. I’ve just recently noticed that I still didn’t have Code Signing running. I’ve done quite some searching and even tried to debug what codesign does when it tries to match certificates. Only then I noticed that even though I had all certificates in place, the problem was that these Certificates needed the private key as well. The fix was as simple as exporting and importing the private key from one mac to the other.

As I’ve had problems with Code Signing before, I decided that it’d be a lot more helpful to not have to google around the webs like crazy over and over again but instead have some app that contains the knowledge and gives proper tips.

The result is CertificateHelper, which you can find over at GitHub. Currently it only supports those problems that I was having (no certificates/no private key).

To add new Problems to the app, simply subclass CertificateProblem and implement the following methods:

  • + (void) load - here you need to call [self registerSubclass] so that the app finds this new problem
  • + (id) problemIfExists - this method returns an instance of the class or nil, depending of if the problem exists or not
  • - (NSString*) htmlDescription – a small snippet of HTML code that describes the problem and how to solve it
  • - (NSArray*) infoObjects – objects that are displayed at the bottom list, if needed (defaults to an empty array
  • - (ProblemSeverity) severity – returns the severity of the problem. If multiple problems occur, this value is used to sort the problems. The one with the highest severity is then displayed to the user.

I think the most problematic part of the implementation is the work with the keychain. There’s the CertificateAccess class right now which tries to simplifies that.

Karsten

by Karsten at December 31, 2011 10:55 AM

December 30, 2011

Gus Mueller

State of the Meat 2011 Edition

It has been a while since I've written about what Flying Meat is up to, so here's a little look back at 2011, a peek at what might be in store for 2012, and general things I've been thinking about lately.

Acorn
First up of course is the release of our updated image editor, Acorn 3. The attention Acorn 3 got was unprecedented for Flying Meat. Never before has our web server been so challenged to keep up with requests until that moment. It was extremely gratifying to see so many people start using Acorn so seriously, and ditching their current image editing software for it.

Layer styles, multi-stop gradients, quickmask, instant alpha, much improved PSD support (which I wrote myself from the ground up), and more. And things just kept on getting better. Acorn is up to version 3.2 now with big speed improvements when working with 15MP+ images, bezier curve improvements, color profile support, native 10.7 goodies like auto-save, versions, and full screen support.

And of course, more updates and improvements will be coming. I absolutely love working on Acorn; there are just so many different aspects to it that it never gets boring to work on.


VoodooPad
Work on VoodooPad 5 has been going on for quite some time now, though it is taking a lot longer to finish up than I had hoped. I sure it'll be worth it in the end. Without going into too much detail, I'm going to talk about one of my favorite big new features:

I killed WebDAV syncing in VoodooPad 5, and replaced it with nothing and it is awesome now.

Ok, that's a bit dramatic. I did replace the sync code with something else but first let me explain quickly why WebDAV syncing is gone in VP5. Apple is killing/killed MobileMe, and with it they killed the WebDAV server which VoodooPad syncing was designed to work with. That's why I've ripped out the WebDAV code.

I should have seen this coming. Honestly, it's not like I didn't know that iTools wait I mean .Mac oops no I meant MobileMe wait it's called iCloud now wouldn't be long for this world. I've come to the conclusion that relying on Apple for any sort of syncing solution or general hosting of your data is probably a bad idea. I really hope I'm wrong, but at least this explains one of the reasons I didn't rush out and add support for iCloud right away in VoodooPad. I want iCloud to succeed. I just don't want to devote a bunch of time building on a syncing solution that's going to be gone shortly.

VoodooPad was born January 16th, 2003. That means that it'll be 9 years old in a few weeks. There are still people out there happily using version 2.0 which came out in 2004. So that means I'm going to need a syncing solution that will be around for at least 5 years. Who can do that? What's the solution?

Well, the solution I've come up with is to ignore syncing and instead redesign VoodooPad's document file format so that it can survive a 3rd party syncing app changing things behind it's back when VoodooPad is running. And I'm so much happier for it now, and I think my customers will be too.

Do you want to put your document in a Dropbox folder and have it sync to multiple computers at the same time, without having to remember to close it? Oh, and have a page automatically update in front of your eyes as it's edited on another machine? Sure, why not? What about if you put it in a source control repository which is shared among multiple people? Can it handle that now? Yes! And VP5 can even tell you which machine a page was last updated on, and by whom.

It's pretty rad. I think stepping back and looking at the syncing problem a different way has been really good for VoodooPad 5. And of course these changes aren't limited to a specific syncing solution. If you want to use your own home-grown app, it'll probably work with VoodooPad 5. All it needs to do is look at a tree of files and make sure the most recently edited one is pushed out to the other machines. This obviously meshes with some of the things that iCloud does as well, but that code hasn't been written yet so I can't make promises at this time.

Kirstin (my wife) and I have been writing the documentation for VP5 in VoodooPad itself. Which used to be a bit of a challenge before these syncing / file format changes. Now we just put a VP document inside a shared Dropbox folder, and edit away. It's a special kind of magic, and I think it'll really open up VoodooPad to a bunch of new users.



FlySketch

FlySketch gets an honorable mention, if only because of a very heated discussion with an App Store representative when I was trying to push an update out.

There's not much to FlySketch. It does what it does and it does it pretty well. I'm happy it still makes a tiny bit of money, but I think the market for this type of utility is dwindling in the face of free alternatives.


JSTalk

On its website I describe JSTalk as "a scripting language for Mac OS X built on top of JavaScript, with a bridge to Apple's Cocoa libraries. You can use it to communicate with other applications just like AppleScript does". And it still does that, but where I've really found it useful this past year is in driving automated testing and building prototypes.

I was a guest lecturer this past summer at UW's iOS and Mac App Dev class, where I talked about automated testing and how I use JSTalk. At some point the instructor, Chris Parrish, mentioned that JSTalk was my "secret sauce". At the time I thought it was kind of funny, but the more I think about it the more I think he might be right. It's amazing how reliant I've become on a tool that I made for a completely different reason than I ended up using it for.

I also like to describe JSTalk as a research project of mine. Developing it has been a great way to explore the Objective-C runtime, and how the stack works (JSTalk uses libffi for parts of the bridge). I've also had a separate branch for a while now where I've been slowly rewriting the bridge that it uses. If I ever get serious about it and finish it, I think I might release it under a different name; something like "CocoaScript" since that makes more sense.


Mac App Store

I love the App Store both as a customer and for Flying Meat's bottom line. I worry about the future though, specifically with sandboxing and upgrades.

I really wish Apple had required sandboxing for applications on day 1 of the App Store. As it is now, I can see that a bunch of applications are never going to be able to be updated or if they are, some of their functionality will have to be stripped out. Acorn isn't affected very much by sandboxing, but VoodooPad certainly is. We'll just have to wait and see what Apple can do to help us out.

My other worry is on paid upgrades, or the lack of having them in the App Store. There seems to be an implicit assumption that by purchasing something in the App Store, all upgrades will be free in the future. But in the long term this isn't a sustainable way to run a business, or at least the type of business that I want Flying Meat to be. I want to write deep, usable applications that have a shelf life of more than a few months. It can take years for an update to come out (hello VoodooPad 5!) and while the warm fuzzy feeling when folks love your application more is great, it doesn't really feel the same as being able to make a living off your work.

Historically for Flying Meat (and most indie Mac companies) the majority of revenue comes in through upgrades and new releases. The App Store doesn't provide this path, so you have to publish a brand new app to have people pay for your work. Recently Adobe took this route with the Elements 8 to 9 upgrade and as far as I can tell, people didn't freak out too much. Time will tell what the best solution is, but for now I just have to remember to stay flexible on this front.


Madeline Kay

On December 9th at 9:41 am, the entire staff of Flying Meat (aka, Kirstin and I) were proud to become parents of the world's newest Mueller. Or as I tweeted a little while later, mueller++. (And I believe this makes Flying Meat an official "Mom and Pop Shop" now).

Being a parent is an interesting and awesome experience so far. The last three weeks seems like one very long night, full of energy and excitement and sleep deprivation.

She's a great kid, and I explain to her patiently about what copy on write is all about, the benefits of a compiled vs. interpreted vs. dynamic language, and I read her a little bit of Advanced Mac OS X Programming every day, but honestly I think she doesn't quite understand pointers yet. Which is fine, because she's too young to get a worker's permit anyway.

December 30, 2011 11:36 PM

NSBlog

Friday Q&amp;A 2011-12-30: Disassembling the Assembly, Part 3: ARM edition

Gwynne finishes off her series on analyzing assembly code with a look at ARM assembly, for all of your iOS needs. Gwynne will be contributing the occasional article in the future as well as a guest author, without my introductions. Watch the Author field at the top of the post to see who's writing what. Without further ado, let's take a look at ARM.
(Read More)

December 30, 2011 09:44 PM