Showing posts with label apple. Show all posts
Showing posts with label apple. Show all posts

Thursday, 28 August 2014

Really useful Xcode plugins

I'm a happy Xcode user, mainly using it for C++ programming. It's a relatively nice IDE (except for when I need to break out the big guns and fire up Vim for heavy lifting).

There are a few plugins that make it an even nicer IDE.

This is as much a note to myself as anyone else.

Backlight
One of the plugins that highlights the current cursor line. I have no idea why Xcode still doesn't do this by default.
https://github.com/limejelly/Backlight-for-XCode

Fuzzy Autocomplete
Makes autocomplete work on steroids, like the Open Quickly fuzzy matching. Nice.
https://github.com/FuzzyAutocomplete/FuzzyAutocompletePlugin

SCXcodeMiniMap
Puts a wee "mini map" beside your scrollbar. Can be useful if you write insane unnavigable source files. (Hint: aim for code that doesn't need it)
https://github.com/stefanceriu/SCXcodeMiniMap

BBUDebuggerTuckAway
Hides the debugger view as soon as you start typing into the editor. Handy. Saves a common keystroke.
https://github.com/neonichu/BBUDebuggerTuckAway

AdjustFontSize
Quite keyboard shortcut for adjust font sizes. Useful as I switch between a retina display and various monitor sizes depending on where I'm working.
https://github.com/zats/AdjustFontSize-Xcode-Plugin

BlockJump
CTRL-] and [ navigate up and down by block.
https://github.com/tyeen/BlockJump

Xcode4_beginning_of_line
Command + left arrow goes to the first non whitespace character, like any sane editor should.
https://github.com/insanehunter/XCode4_beginning_of_line

XAlign
One i'm just trying out. I find this kind of alignment can help reveal code intent, although it adds extra work to maintain. So let's make the computer do the work. Command-shift-X
https://github.com/qfish/XAlign

Saturday, 25 February 2012

Xcode 4 C++ class template updated

I've updated the C++ class template for Xcode 4. You may want to take the latest from github and re-run your installation scripts.

This version now:

  • Allows you to specify a class' (optional) superclass
  • Allows you to add a virtual destructor
  • Can make a new class non-copyable (with hidden copy constructor and assignment operator)
  • Has a sexy icon that matches all the other Xcode template icons.


Could life get any better?

Grab the update from Github here.

Thursday, 23 February 2012

Xcode 4: A C++ class template

Xcode 4 C++ users, rejoice!

It's really annoying that Xcode 4 has never shipped with a template for creating a new C++ class, with header and implementation file.

It lets you create objective C classes with header and implementation. But you always have to create a C++ header file in one step, and then the implementation in a second step.

And, honestly, why do those steps take so long to update the project?

It's not good enough, and so here's the answer...

The Xcode 4 C++ Class Template

I've created a super-simple to install C++ class template and installer.

Grab it on Github here.

Using it is a simple case of running a script and restarting Xcode. I've built in support for Xcode 4.2 and the new Xcode 4.3. The installer runs a number of sanity checks to make sure you're not about to install something dumb on your computer.

Full docs are in the README file up there.

I hope you find it useful. I know I do.

Making your life better, one class at a time...

Thursday, 5 January 2012

Installing Groove Agent 3 (Update from Version 1) on Mac OS Lion

It all seemed so simple. I wanted to install my trusty copy of Groove Agent 3 on a new Mac OS Lion box. The license is on my eLicenser already. So it should just be a quick insert-DVD-and-install job.

Shouldn't it?

Of course not.


#Fail...

I've been using Groove Agent for years now. My version 3 DVD was an upgrade disk from the original version 1. If you run that DVD's installer it says you have to have version 1 installed to upgrade. That seems fair enough.

Except you can't install Version 1 on Lion; it is an old PPC application, and Lion doesn't support Rosetta any more.

It's a deadly circle; I now legitimately own some (not inexpensive) software that I can't install. Pete is not a happy bunny.


Attempt 1: tech support

I sent a tech support email through my Steinberg account. Not with much hope, I have to admit.

That was ages ago. What do you think happened?

Yup, you're right. Not even got a "we've got your message and it's in a queue" reply.

Thanks, Steinberg. Thanks a bunch.

But where there's a will, there's a way...


Attempt 2: Check for updates on the website

The Groove Agent 3 support site has a few update installers available (which, naturally, need the full program installed first to be able to update).

There is also a new "full installer" for the Mac to replace the DVD's installer for first-time users. This shows great promise.

Once downloaded, I run the installer. "Insert the Content DVD" it asks (impolitely). OK, again this is fair enough. The content is enormous, and is on the DVD already so I shouldn't need to download that again. It also proves that I own the product I'm installing.

I insert my Groove Agent 3 DVD into the machine. The installer doesn't recognise it. No message. No hint. No continue button. It just sits there.

Of course, it's looking for the "Groove Agent Installation DVD" not the "Groove Agent Update Installation DVD". Swines.

Absolute swines.

But where there's a will, there's a way...


Attempt 3: Trick the installer

Being a techie I wonder. I wonder how rubbish they really are... Is the installer just looking for a DVD called a certain name?
ln -s "/Volumes/Groove Agent Update DVD"  "/Volumes/Groove Agent DVD"
Run the installer again.

Success!!!

The installer runs, it copies over the content from the DVD and there's my Groove Agent 3 sitting there, ready to run.

Goodliffe: 1, Steinberg: 0, (Steinberg Customer Support: -5)


But, we're not there quite yet

I fire up Cubase, pull in the virtual instrument, and a window pops up asking me to locate the content files. That's the content that the installer just installed. Into a standard place. Didn't it think to look there itself? Sheesh.

That's OK, I'll just use the file browser dialogue that appears to load it. Except that the content is automatically installed into "/Library/Application Support/Steinberg/Groove Agent".

Notice that first bit. Yes, "/Library". The folder that Apple now HIDES from users in Lion so they can't fry in innards of their OS. I physically can't navigate to the content. Genius.

The trick, of course, is to launch Finder, open the "Go" menu, and hold down the Command key. At this point "Library" magically appears in the middle of the menu's list of places you can go to. Select that folder. Navigate to the right directory in the Finder window that appears, and then drag the target directory into the patiently waiting application's file open dialogue.

What a palaver.

But that's it, I now definitely have Groove Agent 3 installed and running.


Whinge

Of course, I have a clue what's going on inside my computer, and was able to engineer this solution based on my experience and a selection of educated guesses. Goodness only knows what the average user would be able to make of this situation.

(Hopefully this blog post will help someone in time. Let me know if it does!)


Endnote

It seems that every time I want to sit down an use my computer to make music, the computer wants some love, wants its nappy changed, or wants feeding first.

 New driver required! Update available! Incompatibility detected!

Technology really can get in the way of being creative.

Thursday, 24 November 2011

PGMidi moved and updated

My popular PGMidi library for MIDI input/output on iOS has moved from Gitorious to a new location on GitHub. (This was requested many times, and who am I to disappoint?)

Please update your repos accordingly.

Over the next few days, I'll also be adding a few new tweaks and features to the project, so stay tuned.

Thanks for all the kind comments and feedback about the code - I'm really glad it's useful. Please do let me know if you've incorporated it in your own project.

Wednesday, 16 November 2011

Xcode 4 keyboard/mouse shortcuts

(I'm posting this here mostly so I don't lose it, although I'm sure it'll be useful to other Xcode 4 users out there.)

There are a couple of really handy Xcode mouse click modifier key combinations that I can never remember when I want them (kind of like a Super Street Fighter key combo).

In particular, you can click on symbols in the code editor and have them open in this editor, in the alt editor, in a new window, or... even... with a (ugly looking) popup asking you where to open (e.g. in a new tab).

Here's the lowdown:


Xcode 4 editor symbol clicks

Modifiers Click What happens
⌘⎇^ + single Open in alt editor
⌘⎇⇧ + single Select where to open (with popup)

+ single Open in this editor
+ double Open in this new editor window
+ single Show help popup
+ double Show help in organiser

⌘⎇⇧ Make alt editor counterpart again (super useful)

Key (what the silly symbols mean) 
Command (cmd)
Option (alt)
^ Control (ctrl)
Shift

Sunday, 9 October 2011

My latest iOS app: The Mahjong Score Book

My latest iOS app is out now (thanks to the iOS 5 SDK having finally gone GM). It's called the Mahjong Score Book and, as the name suggests, is designed to help Mahjong players keep tabs on their scoring in Mahjong games.

Of course, once you have your scores captured inside a wee computer, there's more exciting things we can do with them. So the app allows you to:

  • keep records of all the games you've ever played, with a very clear visual presentation, associated with the date played
  • draw pretty graphs of game progress
  • adopt different rule sets (limit hand value, maximum wins as east wind, etc)
  • export games to databases, as images, or text files
  • automatically roll dice to determine starting positions
  • associate notes with each game
  • and much more.


If you're interested, then check it out in iTunes here.

I appreciate that it's a niche app. It simply something I wrote to fulfil a personal need. Of course, someone else out there is going to find it useful, too. And so I've polished it, honed it, and produced a really very slick app.

I wrote Mahjong Score Book app back in the summer whilst on holiday, playing Mahjong with friends. It serviced its purpose well then and many times since, and I've learnt a few new iOS techniques whilst polishing off the application.

I'm sure that it won't make me millions, but I'm very proud of what I've produced in a relatively short time. If you know someone who plays Mahjong then please point them at it!

Wednesday, 21 September 2011

iOS: Using older SDKs with newer Xcode versions

When you update Xcode versions, the installer automatically removes any old SDKs you have lying around, and replaces them with the latest version.

This is fine behaviour, as the most recent SDKs remain backwards compatible. You can set your project to target older iOS versions. If you do this, the newer SDK features are disabled for you.

However, there are times when you need to use an older SDK.

For example, I am running the latest Xcode with a beta iOS 5 SDK installed. Since this was originally installed on a clean machine, I didn't set the beta install to use a parallel directory and leave the "release version" of the developer tools intact - they simply weren't installed. (Making a parallel install is, in general, the best practice when installing a beta Xcode/SDK set).

Fear not. You can still get your newer Xcode to build with an older SDK, without downgrading your Xcode or making a parallel install:


  • Close any running Xcode instance you have open.
  • Locate the install DMG for an old version of Xcode (e.g. Xcode_3.2.5_and_ios_sdk_4.2_final.dmg, they name them so well) and open it.
  • Do not run the installer!
  • Open the Packages directory in that disk image. It is a hidden directory. Try this terminal incantation: "open /Volumes/Xcode\ and\ iOS\ SDK/Packages"
  • Locate the iPhoneOS and iPhoneSimulator SDKs for the version you want. Run just those pkg files. (e.g. I ran the iPhoneSDK4_2.pkg and it's matching iPhoneSimulatorSDK4_2.pkg)
  • Make sure you specify the /Developer directory as your install location. If you don't, the SDKs will be installed in your root directory, under the /Platforms directory; you'll have to manually copy them into /Developer/Platforms yourself.
  • Now, re-open Xcode. If the SDKs installed in the right locations, they will be selectable in your project now.


Friday, 22 July 2011

Xcode: Determining the current SDK version in a script

In an iOS XCode project, I needed a shell script target (aka "External Build Tool" target) that invokes other scripts which in turn build stuff using Xcode. Curiously recursive.

Now, I needed to pass that script the current iOS SDK version being used so it could arrange its own build malarkey, and get all its SDK ducks in a row.

There is no environment variable for that defined by Xcode. Bah. The closest you get is the current deployment target version, which appears in $IPHONEOS_DEPLOYMENT_TARGET.

That seems like rather a large omission.

You do get given $SDKROOT which is a file path to the current SDK. But that's not quite the same thing as a simple version number.

Never, fear, shell gibberish to the rescue. This is what I came up with:

SDK_VERSION=$(echo $SDKROOT | sed -e 's/.*iPhone\(OS\)*\(Simulator\)*\([0-9]*.[0-9]*\).sdk/\3/')

If you know a better way to do this, I'd love to know.

Thursday, 9 June 2011

The Enigma Continues

Version 1.4 of my cool iOS puzzle game, The Enigma Squares, has been released. Check it out here. In this version, I present you more than 80 new levels, with a number of fun, a number of fiendish, and a number of speed-trial puzzles.

Check it out on iTunes now.

I've also released a new game. The Enigma Kids is a family-oriented puzzler. With child-friendly puzzles, great graphics and lots of fun, everyone will enjoy this game.

Check out The Enigma Kids on iTunes here.

Let me know what you think!

Wednesday, 25 May 2011

Optimising Parallels performance for development

I use the wonderful Parallels 6 to perform all my Windows development. (I find working on a Mac makes Windows development a lot more bearable - at least for this old Unix-head).

In general, Parallels works superbly (and is reported to perform a lot better than VMware, if you believe the hype). However, for operations like rebuilding a large, complex C++ source tree, the virtual machine performance lagged a long way behind a native machine. Large compiles can take more than twice the time of a real Windows PC. Clearly, this taxes the host machine hard: hammering CPU, memory, and disk at once.

This problem exhibits on my four core Mac Pro with as many resources as I could throw at the problem, as well as on my more humble MacBook Pro.

I performed a number of experiments to work out how to fix this:
  • Tweaking the allocated CPU cores, memory, and disk space
  • Tweaking the settings for the virtual hard drive
  • Network building from host mac file system
None of these made much difference. In fact, I was somewhat surprised that operating on a networked disk was fantastically slower. That was a shame, as it would be advantageous to share a single source tree with my Xcode development for cross-platform development. (As it happens, I manage this by having a Windows-side git clone of the git repo on the Mac side).

However, I've now managed to get my compile times right down. Here's how.

Do bare in mind that these tweaks are in addition to the obvious steps you should take with any machine:
  • Have as fast a CPU as possible.
  • Throw as many CPU cores as you can at the problem - builds can be parallelised very easily.
  • Throw as much memory as you have at your disposal at the problem.

1. Swap the swap

The factor slowing me down the most turned out to be the Parallels virtual disk that Windows was running on. First, the swap file was hosted on it. So I made a second virtual disk, and pushed the virtual memory off onto it. That made a small, but appreciable difference.

2. Only ever use fixed size disks

In your disk settings, do not use automatically expanding disks. That's just another overhead for the computer to manage whilst accessing disks. Create a fixed size blob of disk space and run with it.

3. Use a virtual SCSI disk

This is the big one. So pay attention at the back.

It seems that Parallels can emulate a SCSI disk far, far faster than it can emulate and IDE disk. However, it defaults to creating IDE disks. I believe that this is to make installation of Windows simpler, as you don't have to fettle with custom SCSI drivers during installation.

You can't just switch the main disk from IDE to SCSI, though. Windows has a minor eppe and refuses to run if you do that. I couldn't be bothered to wipe it all and try to reinstall on a new SCSI virtual disk, so I added yet another large SCSI virtual disk for my source trees and checked out onto that disk.

Remember: it's a fixed size, non-expanding SCSI disk.

With this configuration the build simply screams in comparison to building on the virtual IDE C: drive.


I hope you find this information useful!

Friday, 13 May 2011

iOS: The Enigma Squares (1.2)

The Enigma Squares version 1.2 is available in the App Store now. Amongst other excitement, it adds Game Center support. You can now share your achievements and high score with other players. Woo hoo.

So far, I'm very impressed at how far players have got. I'm glad the game is providing an interesting challenge!

More interesting updates are in the pipeline...

Thursday, 12 May 2011

iOS: Packaging apps store submissions from the command line (my zip broke)

How to package iOS apps for submission to iTunes Connect from the command line.

I'm a firm believer in the power of the command line. I like my GUI tools as much as the next man (as long as he's a bearded Unix guru from the 1970s). But for sheer power, repeatability and flexibility you just can't beat a good script.

Which is why every project I work on has to have a simple single script that configures, builds, and packages the code, creating a well-named payload that can be installed/submitted/deployed. I feel a lot safer about the quality of a release if it's generated by a tried-and-tested script, rather than by a series of button clicks in a series of separate tools.

You know it makes sense.

iOS projects submitted to the App Store are effectively zip files with a particular directory structure. You can drive Xcode with the xcodebuild tool to build and link your distribution version of an iOS app.

However, whenever I tried to zip the project up from the command line, the Application Loader utility (what you use to submit your app to iTunes Connect) just gave bizare errors.
The CodeResources files must be a symbolic link to _CodeSygnature/CodeResources. Make certain that the bundle is on a locally-mounted volume...
Application failed codesign verification. The signature was invalid, or it was not signed with an Apple submission certificate.
Well, that's nice gibberish. Not.

However, a zip archive created using the Finder's "Compress" menu entry worked fine. Very frustrating.

Don't fear! I fixed it. Here's how to do it in your very own build script: your zipper is broken; throw it out. Use ditto instead. Here's the line from my build script:
APP_FILENAME=/path/to/build/Application.app
PACKAGED_FILENAME=MyGoodPackagedFilename.zip
ditto -c -k --keepParent --rsrc "$APP_FILENAME" "$PACKAGED_FILENAME"

Enjoy your build scripts.

Wednesday, 4 May 2011

iOS: The Enigma Squares

My first personal iOS project has finally seem the light of day. Please welcome into the world The Enigma Squares. It's an interesting (and dare I say it) entertaining block-moving puzzle game.

Check it out on iTunes here.

I've produced plenty of iOS projects for my company. But this has been an entertaining personal side-project, to keep me busy through those long spring evenings.

It is actually a version of a game I first wrote (ahem) 20 years ago. Back then it was written for the Acorn Archimedes, and I sold it to a magazine for inclusion on their cover disk (remember those?) This version is a little more polished, a little more challenging, and a lot more fun.

The code's a fair bit better too, but that's another story. One that I'm sure I'll tell one day...

Let me know what you think of it!

Tuesday, 5 April 2011

iOS: A Fading AVAudioPlayer

In a recent iOS project I needed to play some background music and have it fade smoothly in and out when it starts and stops.

The "play some music" part is easy. iOS give us AVAudioPlayer in the AVFoundation framework. Super sweet and easy to use. However the "fade in and out" bit doesn't come for free.

The canonical solution suggested on the web is to hand-craft some nasty looping logic to get the job done. This is 2011 and we can do better than that. And, indeed, here it is, in all it's Objective C glory.

My solution makes interesting use of categories, associative references and blocks. It's worth strolling through the implementation if you're interested in any of these. If not, you can just grab my code from the Gitorious project and use it in blissful ignorance.

1. Add a category

First, we open up our own category on the AVAudioPlayer class and define the methods we would ideally like the class to provide:
@interface AVAudioPlayer (PGFade)
- (void) stopWithFadeDuration:(NSTimeInterval)duration;
- (void) playWithFadeDuration:(NSTimeInterval)duration;
@end

That's the joys of categories - you can extend existent classes in your own application easily to make it look like methods were part of the original class interface. The common convention is to save this in a file called "AVAudioPlayer+PGFade.h".

Now client code can create a bog-standard AVAudioPlayer object, and call my new methods as if they were part of the base interface:
AVAudioPlayer *player = [AVAudioPlayer alloc] initWith...]; // however you want it set up
[player playWithFadeDuration:2.0];

2. Associative References

Now, our implementation of this is going to require some instance variables (ivars) to work with. The problem with categories is that they only allow you to add methods - you can't extend the set of instance variables defined in the class' @interface.

Or can you?

Associative references to the rescue! This is a handy Objective C runtime facility that allows you to associate another object with an existing object, with a lookup system very much like a dictionary - referenced by a void* key value.

The associated object is lifetime-managed with the original object, so when you release the parent, all associated objects are also released.

Using this facility we can "graft on" some of our own private ivars to the original class. Dirty, but effective.

One of the variables I need is a boolean variable tracking whether a fade is currently in progress. I actually expose this as a property called fading in the category interface. The implementation looks like this:
@implementation AVAudioPlayer (PGFade)

static char fadingKey;

- (BOOL) fading
{
NSNumber *number = (NSNumber *)objc_getAssociatedObject(self, &fadingKey);
return number && number.boolValue;
}

- (void) setFading:(BOOL)fading
{
objc_setAssociatedObject(self, &fadingKey, [NSNumber numberWithBool:fading], OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

That's relatively simple. We just use two objective C runtime functions,  objc_getAssociatedObject and objc_setAssociatedObject. For the association we use the address of a static variable - this will be an unambiguous value in the whole program. The object we store is an NSNumber object initialised with the value of our boolean.

It's a simple and effective trick.

3. Blocks

The final bit is for bonus points. You can see the implementation of my fade routine in the example project code itself. It's pretty simple - I just initialise the fade state in a few more "associative reference" variables and schedule a method call with performSelector:withObject:afterDelay. Each time this is called, I adjust the AVAudioPlayer's volume, and schedule a new call if one is needed.

However, to truly perform a stop with fade, we need to ramp the audio volume down, and when it gets to zero stop the player. This could be achieved with some more state variables and clumsy logic, but iOS now provides us with blocks which are perfect for this kind of activity.

For sanity, I typedef a block (closure) type. The syntax practically identical to typedefing a pointer-to-function in C, but with ^ instead of *.

typedef void (^AVAudioPlayerFadeCompleteBlock)();

I make one more associated variable of this type, the block to call on completion:
- (AVAudioPlayerFadeCompleteBlock) fadeCompletion
{
return (AVAudioPlayerFadeCompleteBlock)objc_getAssociatedObject(self, &fadeCompletionKey);
}

- (void) setFadeCompletion:(AVAudioPlayerFadeCompleteBlock)completion
{
objc_setAssociatedObject(self, &fadeCompletionKey, completion, OBJC_ASSOCIATION_COPY_NONATOMIC);
}

Note that blocks can be traded like objective C objects. You can retain, copy, and release them. In this case our code needs to take a copy (using the OBJC_ASSOCIATION_COPY_NONATOMIC flag). This ensures that any set block exists on the heap (persistent) rather than the default on-the-stack location.

Now, the internal "audio fade" routine need only check whether there is a completion block registered when the fade is complete, and if so call it:
    if (fadeIsComplete)
{
self.fading = NO;
AVAudioPlayerFadeCompleteBlock completion = self.fadeCompletion;
if (completion) completion();
self.fadeCompletion = nil;
}

Invoking a block looks just like calling a function. Once called, we clear out the stored block in case it is holding other resources that should be released.

This provides an elegant way to perform any arbitrary action after the fade is complete.

That's all, folks!

The trio of Objective C facilities used here work together well to provide a simple and elegant solution to a problem that is often achieved with complex "strings-and-glue" logic. Check out the entire project on Gitorious here.

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 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

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.