Vincent Gable’s Blog

February 9, 2009

Color Blindness

Filed under: Accessibility,Design,MacOSX,Programming,Usability | , , , ,
― Vincent Gable on February 9, 2009

Roughly 10% of men are color blind to some degree. You need to be sure your interfaces are accessible to them. (Unless you are designing exclusively for women I suppose, since women are about 20x less likely to be color blind.)

Sim Daltonism is the best way to test an interface on Mac OS X I’ve seen.

Here is a web-based colorblindness simulator. Here is another. Personally I prefer a native program though. It’s faster and more versatile.

If you are curious, you can test yourself for colorblindness. I have no idea how accurate that test is, but since different displays and operating systems usually show colors differently I’d be a little skeptical.

ADDITION 2009-10-11: WeAreColorBlind.com is a website dedicated to design patterns for the colorblind.

December 12, 2008

One assert() You Need?

Accidently disconnected outlets in shipping Cocoa apps are legend.

Jonathan “Wolf” Rentzsch

From what I can tell assert() is slowly going the way of the goto in the programming world. Exceptions, unit-tests, and other modern software engineering practices, seem to have a better answer for testing something at runtime, and ensuring that you never enter a bad state.

But there is one case where I think you should put an assert() (and not an NSAssert()) in your code: in awakeFromNib assert() that every IBOutlet is connected.

It’s surprisingly easy to accidentally disconnect something in Interface Builder, or rename something in Xcode. I’ve done it before. More then once. assert()-ing IBOutlets has saved me a lot of debugging time.

Hard to Find In Code

An IBOutlet that is not connected is nil. Because Objective-C quietly ignores messages sent to nil, it’s very easy not to notice the problem for a while; then spend a long time debugging a side effect of the issue.

Hard to Find By Eye

A widget in a nib/xib file that is not connected will never change it’s state. But it typically it has a reasonable initial state. This makes it difficult to detect disconnected IBOutlets by eye, because things will look right until they are supposed have changed, but the eye is drawn to change, and de-emphasizes unchanging things.

Why assert()?

As Wolf says, the worst-case-scenario is that right before release you make a trivial change in Interface Builder (“We can’t ship with that window saying “Claculator”), an IBOutlet gets disconnected, and nobody notices in time.

But an assert() failing will be noticed with casual testing, and triggered if the nib is loaded at all, even if the series of interactions needed to cause the widget to change state are not performed.

An assert() is very light-weight and easy to do. It’s exactly one line of code, and you don’t have to add a unit testing framework to your project to do it. And that makes it perfect for test projects. I’m a believer in getting a feature working in a test project first, then copying it into your real product. (Honestly I don’t do it as much as I should, and I almost always kick myself for it.) It lets you test and learn without hacking-up your product. Plus, new things are often easier to try without the weight of a big code base.

Speaking of unit tests, I don’t have complete faith in them here. That’s because a disconnected IBOutlet in a shipping program is fundamentally an issue with the way the release-build is configured. So anything that’s not testing the the actual release build is not exhaustively testing for this problem. an OCUnit-test can catch a disconnected IBOutlet if it is loaded into the application. But in my experience, such tests are cumbersome. Running them involves launching your full application, and programatically manipulating it. This can take a while to run since all the UI will be displayed and animated. And you have to be careful about state since each test will be changing the state of your program.

Ultimately, assert() just seems to be optimal, in dependability and simplicity, for catching a disconnected IBOutlet.

But I suspect there’s a better way I don’t know.

How do you catch IBOutlets?

May 25, 2008

Objects that Won’t Hide

Filed under: Bug Bite,Cocoa,Interface Builder,MacOSX,Objective-C,Programming |
― Vincent Gable on May 25, 2008

NOTE: Although this specific Bug Bite is about NSTextView, and the “hidden” property, the same underlying issue applies to other interface-objects (NSTableView, etc.), and different properties, like size.

Problem

If you send a setHidden:YES message to an NSTextView, and it’s text disappears, but the view itself (white box) stays visible here’s the problem, and the solution.

It turns out that if you created the NSTextView by dragging it off the pallet in Interface Builder, then it’s not an NSTextView. It’s an NSTextView wrapped inside an NSClipView inside an NSScrollView. The NSScrollView is what puts up the scroll-bars if the NSTextView gets really big; the NSClipView helps make the scrolling work.

So if text is your IBOutlet to your NSTextView, then when you say [text setHidden:YES];, the NSTextView is hidden, but the the total package won’t disappear, unless you hide the NSScrollView as well.

Solutions

You can send the message to NSScrollView containing text, like so:
   [[text enclosingScrollView] setHidden:YES];.
This will hide everything inside the NSScrollView, including text.

Another solution is to create just an NSTextView in Interface Builder. To do this, put an NSView in your interface (it’s called a “Custom View”,in the Interface Builder objects pallet). Then select it, bring up the object inspector (cmd-shift-i), choose the “custom class” from the category menu at the top, and select NSTextView from the list of subclasses. This puts an NSTextView in your interface, without the surrounding clip and scroll views. Unfortunately, it also means you can’t configure it in Interface Builder, beyond resizing it. That’s why I’m not partial to this approach, although I have used it.

Thanks to ZachR for suggesting enclosingScrollView.

Powered by WordPress