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.