Dynamic Binding to Checkboxes
Cocoa Bindings are great, but what do you do when
you need to bind checkboxes to an arbitrary array of data?
Some time ago, I had a requirement to present a list
of checkboxes that corresponded to an arbitrary array of objects. The array
could be changed by the user, though not during the time that the checkboxes
were presented.Previously this had been
done by heavy use of switch statements. This did not seem like a good idea to
me, and I was using Cocoa bindings in most of my UI code. So I decided to see
if I could get bindings to work in this case. I did a lot of searching on
dynamic bindings and checkboxes and arrays, but came up mostly empty. I finally
stumbled on an article at Cocoa Love that gave me the clue I
needed.It turns out I was looking for
valueForUndefinedKey and setValue:forUndefinedKey:. With that knowledge, I was
able to craft a solution. This may be a fairly unusual problem that no one else
will have, but I thought I would blog it anyone in case some other poor soul
found themselves in need of it. I'm also including the full code for a sample
app making use of this technique. As the license in the project says, I make no
warrant about its usefulness, etc. As far as using the techniques themselves,
feel free to use them without
attribution.So, here's how it works.
First, I have a class for my objects. In the case of my sample code, it's
fairly
simple:@interface
Dummy : NSObject
{ NSString
*
name; NSNumber
*
include;}@property(copy)
NSString *
name;@property(retain)
NSNumber *
include;I'm using Objective-C 2.0
properties, so that's about all there is to the class, just an @synthesize in
the implementation.In the class that
controls the checkboxes, I init the array with the contents of a text file.
Obviously, the data could come from anywhere. In interface builder, I created a
view where the checkboxes would go, and I instantiated an NSObjectController. I
connected the content outlet of the NSObjectController to my checkbox
controller. I also conntected the checkbox view and the NSObjectController to
outlets in my checkbox controller.In
awakeFromNib, I call a simple layout routine which iterates over the source
array and creates a checkbox for each item in the array. In addition to the
placement calculations, I also bind the checkbox to the
NSObjectController: NSString
* keyPath =
[NSString
stringWithFormat:@"selection.%i",[dummies
indexOfObject:aDummy]]; [switchButton
bind:@"value"
toObject:theController
withKeyPath:keyPath
options:nil];I
then implemented valueForUndefinedKey and
setValue:forUndefinedKey:-
(id)valueForUndefinedKey:(NSString
*)key{ NSUInteger
theIndex = [key
intValue]; Dummy
* aDummy =
(Dummy
*)[dummies
objectAtIndex:theIndex]; return
[aDummy
include];}-
(void)setValue:(id)value
forUndefinedKey:(NSString
*)key{ NSUInteger
theIndex = [key
intValue]; Dummy
* aDummy =
(Dummy
*)[dummies
objectAtIndex:theIndex]; [aDummy
setInclude:value]; [self
scanForIncluded];}scanForIncluded
is a routine I use to display the results so you know I'm not lying. You would
probably put in whatever action you need to do when a checkbox is
clicked.Now, I'm sure there are better
ways to figure out the keys, but this method seems to work well enough for my
purposes.Here are some examples of the
code at work. I started with a list of dummies
(literally!):Charlie
McCarthy tMortimer
Snerd fJerry
Mahoney tKnucklehead
Smith fThe t and f represent true and
false. You might say the initial condition includes then dummies based on being
a wise-guy. So here is the starting
condition: As
expected, the correct dummies are checked and included in the table on the
right. Now we click Mortimer's
checkbox: So
far, this could have been accomplished by normal static methods. Someone comes
in and says we need a more recent dummy, so we add Buffalo Billy to our list of
dummies. (He gets a t, too, because he's also a
wise-guy):Charlie
McCarthy tMortimer
Snerd fJerry
Mahoney tKnucklehead
Smith fBuffalo
Billy tThe next time we run, we
get: so,
our technique is, in fact, dynamic.A
word about the sample code. I am using Garbage Collection in this example
because I am trying to learn about it and I didn't have to use retains and
releases in the code. I'm getting compiler errors, but it still runs. I do not
present this as a good way to do things, but it let me get this out
quickly.On the good side, this sample
code also shows how to read and parse a tab-delimited text file.
Here's the sample code:
Checkboxes.zip
Posted: Thu - March 12, 2009 at 08:50 AM
|
Quick Links
Calendar
| | Sun | Mon | Tue | Wed | Thu | Fri | Sat
|
Categories
Archives
XML/RSS Feed
Search
Extreme Tracking Web Counter
Statistics
Total entries in this blog:
Total entries in this category:
Published On: Mar 12, 2009 03:46 PM
|