Friday, 18 March 2011

iOS: Prettier auto-rotation

A simple Friday iOS coding tip for you:

When your app auto-rotates do you have an unsightly white frame appearing around the edges of your beautifully hand-crafted UI?

The default Xcode UIView-based project template creates a UIWindow with a white background. The white frame on rotation is the background of the UIWindow peeping out behind your UIView subview.

The simple fix to your UI woes: make the UIWindow background clear (either in the XIB file, or just before your call to [window makeKeyAndVisible].

Simples!

Wednesday, 16 March 2011

[ios] iOS Development talk slides available

The slides from my popular iOS development talk, last given at BCS Edinburgh are available for you to view on Slideshare.

To view them, follow this link.

Friday, 11 March 2011

Writing: Many-festos

The March issue of ACCU's C Vu will be hitting doormats any moment now. It contains the latest installment in my Becoming a Better Programmer column. This one's called Many-festos, and is a slightly irked rant on the proliferation of software development "manifestos" that have sprung up recently, and some of the arguments that have arisen around them.

The PDF version of the magazine will be available for download by ACCU members shortly.

Friday, 11 February 2011

[ios] Dynamic cast in Objective-C

In C++, you can cast a pointer between two (related) types, and ask the runtime to check if the conversion is valid using dynamic_cast. Usually in C++ the use of dynamic_cast is a sign that you're doing something wrong - good C++ design generally avoids such constructs.

However, Objective-C is a far more dynamic language, and often you do need to do something similar. The runtime provides all the smarts for this, but doesn't deliver it in a simple one-liner.

So let's fix that...

Wondering if a view controller's view property is a UISwitch? Try this:
UISwitch *switch
= objc_dynamic_cast(UISwitch,viewController.view);
if (switch) NSLog(@"It jolly well is!);
That's nice, isn't it? Here's how:
#define objc_dynamic_cast(TYPE, object) \
({ \
TYPE *dyn_cast_object = (TYPE*)(object); \
[dyn_cast_object isKindOfClass:[TYPE class]] ? dyn_cast_object : nil; \
})

For the curious, here's what's going on...

First, we define a preprocessor macro using a gcc extension: the macro compound statement. That's the ({ ... }) block. This is a little dirty, and I certainly wouldn't use a gcc-specific extension in my "portable" C++ code. However, Objective-C lives in a different ecosystem, where you can guarantee that compound statements will be supported. So we'll go with it.

The reason I use a compound statement is to define a "local" variable - dyn_cast_object. This is just to cover the macro's back - it needs to mention the name of the object argument twice, and if that expression has side effects you'd get twice the number of side effects otherwise. Side-side effects. So I store the argument in a local variable to avoid this. (Most of the time this is unlikely to be an issue, but if you ever got bit by this problem, you'll appreciate having done it right the first time).

Then we simply ask the object is it is of the class of our cast's destination type, using NSObject's isKindOfClass: selector.

Not too complex. And you could do the isKindOfClass: dance wherever you need to cast. But it's nice to write the macro once and then write clear code that reveals it's intent.

[ios] Cancelling a UIGestureRecognizer

You have a view with a handful of gesture recognizers. Perhaps some of them are chained together with requireGestureRecognizerToFail so that they interact well.

Sometimes, you still need to respond to other user interaction, and cause any pending gesture to immediately fail. There isn't an API for that.

I had this issue when my application's "timeline" view, which was using pinch, tap, and pan gestures in concert, needed to grow temporary UIButton subviews. The buttons worked fine, but pressing them also triggered the gesture recognizers - very confusing for the user.

Here's the solution.

How to forcibly cancel a UIGestureRecnogizer

Add a new category (I call it "Cancel") to the UIGestureRecognizer class extending it with a new cancel method:
@interface UIGestureRecognizer (Cancel)
- (void)cancel;
@end
And implement the new method like this:
@implementation UIGestureRecognizer (Cancel)
- (void)cancel
{
self.enabled = NO;
self.enabled = YES;
}
@end
The simple trick of switching off and back on a gesture will force it to cancel.

Then for bonus points, I add a category on UIView to make it easy to cancel all the gestures attached to it. I tend not to hold the gestures as member variables in my view controller because there isn't any need to do so - they are retained by the view you attach them to.
//-----------------------------------------
// In .h file:

@interface UIView (CancelAllGestures)
- (void) cancelAllGestureRecognizers;
@end

//-----------------------------------------
// In .m file:

@implementation UIView (CancelAllGestures)
- (void) cancelAllGestureRecognizers
{
for (UIGestureRecognizer *gesture in self.gestureRecognizers)
{
[gesture cancel];
}
}
@end
This new method could then be easily used in my main view controller, by attaching it to the my UIButton objects as a new action:
[button addTarget:myViewWithGestures
action:@selector(cancelAllGestureRecognizers)
forControlEvents:UIControlEventTouchDown];

Categories are a useful Objective C feature that really do help to make your code more clear and readable. Here they provide the means for another simple solution. I hope you find it useful.

Friday, 4 February 2011

So you need some software?


This is your handy cut-out-and-keep guide to software development.

Click on the image for high-res version.

PGMidi updated

Attention PGMidi users! I've updated my MIDI Monitor example project again, this time making PGMidi less of an example, and a lot more useful for general use.

Check it out on the GitHub project page (historical note: it was hosted on this gitorious project page, but that is now deprecated in favour of GitHub).

What's changed:
  • The PGMidi class now allows you to determine what MIDI sources and MIDI connections are currently available.
  • It allows you to determine whether the source/destination is a networked session, or a physically attached device.
  • The "events" PGMidi sends through the delegate interface are far more explicit.
  • I've added some useful tools to find a matching source/destination pair if you want to speak to one specific MIDI device.
The MIDIMonitor example application continues to show how to use the PGMidi API.

Thanks for all the comments and feedback I've received about this code. I hope you find these updates useful.