Quick help for array of checkboxes, please…

Hello,

Ok, it’s late and I can’t think clearly, as I try for two hours now to find a solution and get no results.

I have a set of checkboxes in a dialog, which the user can put on or off. Before calling a method, I want to make a list with the check boxes’ titles whose value is on.

I could make so many properties as I have check boxes, and then add them one by one to my list; I could test inside the method by getting each box like this:

If myBoxA as integer is one then (add its title to the list)
If myBoxB as integer is one then (add its title to the list)
If myBoxC as integer is one then (add its title to the list).

The single idea makes me sick. I have 16 checkboxes.

My wish is to have a dictionary where I could have {value: true, name: “Checkbox one”}, make an array of them, and then bind the whole thing in IB (to each control), so
a) the title of the checkboxes will be set programmatically by the array;
b) before calling the next method, I could get a list of titles whose box is on

I’m fighting against Xcode for too long to hope finding suddenly the solution. The concept is clear to me, but as usual I get errors “cannot create BOOL value from.”, or titles like “( )”. To put it politely, I despair.

Any ideas?

I don’t think you can do what you want with bindings, but there are easier ways than doing them one by one. You could have the check boxes in a matrix, and just loop through the matrix’s cells() adding the title of any check box whose state is 1 to an array. Or, you could group them in an NSBox, and loop through the box’s subviews() (actually it might be the box’s subviews()'s lastObject()'s subviews() because I think a box has a content view as its one top level view). For that matter, you could just loop through the dialog window’s subviews, checking that the subview is one of your check boxes (by tag or class type depending on what else you have in the dialog).

I know you’re very fond of bindings, but they’re not the answer to everything.

Ric

Hello Ric,

I didn’t even thought to parse the subviews. Thanks for the idea! You’re right, a NSBox has a NSView, which does not appear in IB list.

My checkboxes were into a NSBox already. That’s fine, I tested your code and got whatever I wanted: title, value and tag. a dream!

In fact, I love bindings, but what I love even more is a cleaner and faster code. Binding are a means, not an end!

Well, more than time to sleep. Thanks again!

Regards,

Hi,

think object oriented ! :wink:
solution with a few lines of code, assuming there are 4 checkboxes (alphaBox, betaBox, gammaBox, deltaBox)


script SKAppDelegate
	property parent : class "NSObject"
    
    property alphaBox : missing value
    property betaBox : missing value
    property gammaBox : missing value
    property deltaBox : missing value
    property arrayController : missing value
    
    property array : {}
	
	on awakeFromNib()
        set my array to {my alphaBox, my betaBox, my gammaBox, my deltaBox}
        arrayController's setFilterPredicate_(current application's NSPredicate's predicateWithFormat_("state == 1"))
    end awakeFromNib
    
    on buttonPushed_(sender)
        arrayController's rearrangeObjects()
        set itemList to arrayController's arrangedObjects's valueForKey_("title")
        display dialog (itemList's componentsJoinedByString_(return) as text)
    end buttonPushed
	
end script

connect the boxes to the appropriate outlet properties and bind their actions to the buttonPushed_() method.
Add an NSArrayController in InterfaceBuilder. Connect it to the arrayController property and bind content array to App Delegate.array

If you’re using a table view, you could bind the table view column directly to arrayController.arrangedObjects.title

Hi Stefan,

Meanwhile, I had another idea:

In applicationWillFinishLaunching_, get the subview list as Ric said (ok, it’s a bit procedural to search under the skirt of an object, but the subviews instance variable is not private.)

Set checkBoxesArray to gCBBox's subviews()'s lastObject()'s subviews() -- store this in a NSMutableArray property.

Now use your checkBox method (more object-oriented indeed), assuming each CB calls it when clicked:

on checkBoxClicked_(sender)
     if sender's integerValue is 1 then  -- maybe we should coerce a bit here, in ASOC it's a pain.
          checkBoxesArray's addObject_(sender)
     else
          checkBoxesArray's removeObject_(sender)
     end if
end

So I have always a updated list of “ON” check boxes – with the correct |count|().

In a pure object-oriented applications (for the moment I only dream of it), I suppose I would add some methods to the NSMutableArray, or better to the NSBox, some of a

  • (NSInteger) myNSBoxAddOrRemoveThisCheckBoxFromSelection: (id) aCheckBox

with the number of remaining checkBoxes as return value, instead of sending a |count| message.

But this is a different design. Sorry, I’m still thinking in terms of this BASIC-like ApplescriptObjC.

Regards,

The subview solution requires a loop

using the arrayController you could add a method


on filteredTitleList()
	arrayController's rearrangeObjects()
	return arrayController's arrangedObjects's valueForKey_("title")
end filteredTitleList

the method returns always the list of titles of the activated check boxes.
No if`s, no loops

KVC/KVO is almost as cool as bindings :wink: (although it’s technically the same thing)

Stefan,

I hate loops just less than “goto” statements. :slight_smile:

Ok to set a predicate to the controller. I suppose rearrangeObjects() does make some kind of loop, that’s were the predicate is triggered. but not in my code :wink:

So, when a button is clicked, it calls the method:

on buttonPushed_(sender)
arrayController's rearrangeObjects() -- which runs the "loop" and adds/removes the sender from the arrayController's array according to the predicate's condition ("state == 1")
end buttonPushed

And that’s it? Later, I suppose that if I want to get the tag of one of my controls, I could make

set theTag to (item xxx of arrayController's arrangedObjects)'s tag as integer --?

Ok, I’ll give it a try. I knew you would eventually convince me with predicates.:slight_smile:

Thank you!

No, again think object oriented! The predicate sends the message “tell me if you match the predicate otherwise shut up” to each object in the controller (simultaneously!).

If you want the user to select one or multiple items in the list, use a table view, bind arrangedObjects.title to the column and get the selection with bindings (Selection Indexes) or one of the methods selectedObjects() or selectionIndexes()

Hi Stefan,

The NSPredicate works perfectly well. As you said, no loop, no if. Yeah, 10 lines of ugly code may die.

After you have tested something, it’s incredible how the docs become clearer. So this is the arrangedObjects of an array controller: a given array, appropriately sorted and filtered, not the content array itself! Ok, it was written from the beginning in the documentation, but now it sounds more familiar.

Thank you Stefan, very elegant solution. I wonder how many lines of my code may be avoided if I had the knowledge of cocoa that certain of you have here.

Regards,

Hello again,

So. Stefan’s NSPredicates are completing Ric’s methods as long as data are in arrays. But there are data shown in a NSOutlineView, which is controlled by a NSTreeController.

The Tree Controller has no methods to install a NSPredicate – and no arrangeObject method. Only sort descriptors to rearrangeObjects(). So, to gather selected items among the tree, I have to do this:


set selectedVerbList to {}
repeat with aNode in theData
    repeat with aVerb in aNode's mutableChildNodes()
        if aVerb's theState() as integer is 1 then set selectedVerbList to selectedVerbList & (aVerb's representedObject())
    end repeat
end repeat

Is there a way to avoid THIS loop?

Regards,

NSTreeController inherits from NSObjectController which has indeed a method to set a predicate.
But as the data model of NSTreeController could be a nested list, the predicate is supposed to be used with Core Data

Ok, what I have just read here http://macscripter.net/viewtopic.php?id=38012 tells me to keep my nice REPEAT/IF pair. :confused: as there is less than 100 items to filter, it’s not worth it.

Thank you anyway – I have tried.

Core Data is not trivial, even in Objective-C

Hello.

Since this was originally about an array of checkboxes, and my perils stems from a matrix of checkboxes (NSButtonCell), I feel free to share my experiences, hopefully someone finds them enlightening. Which is the purpose of writing this post.

I had an object full of booleans, that I wanted to bind to a matrix of checkboxes in a control, and tried to work it out with an objectController.

The correct solution was to bind every single button cell up to the file’s owner’s containerObject’s property.

In english: My object with proerties is an object of File’s owner, I bind each property using the full keypath (object.property) the respective button cell in the matrix.

I had to click some and copy and paste some, but it is a much cleaner solution than anything else.

My second experience, is that in order to get to bind the selection of an ArrayController, say to another controller in your view, then you have to bind the selection index of its contents to the Array controller first. Only then can you use the selection of the ArrayController.

I hope that it makes sense, I haven’t seen this spelled out properly anywhere. Maybe Stefan has. :slight_smile: