Friday, May 16, 2014

CPRadio: Implementing Radio Boxes

This tutorial extends a previous tutorial on CPButtons.  Please review that tutorial before continuing on to this project.  We will use the same source files from that tutorial, and it is understood that you may have modified those files already while working on one of the other projects, the heart of this tutorial should remain unchanged.

What sets our expectation of radio buttons apart from other controls is that we expect them to work together in groups.  As we look through the class file, we have the option of attaching it to a radioGroup, so we need to understand radio groups a little more before we continue.

CPRadioGroup

Image taken from: Cappuccino Project's API Documentation
The CPRadioGroup is not a control object.  It is also not a view object.  It is merely a means of grouping radio buttons together into one logical construct.

Because it is not a view subclass, the radio group has no visual display, and it has no effect on the display of the radio buttons within it.  We still must position those buttons manually.

CPRadioGroup has a method called _addRadio:(CPRadio), which is hidden from the documentation as a result of the '_' character which starts the name.  This function is difficult to find, because CPRadioGroup does not have its own definition file.  It is defined within the CPRadio.j file.

We can call _addRadio:(CPRadio) directly, but the developers hid this function for a reason.  Rather than use this method, we will define our radio group, and we will use the CPRadio method -(void)setRadioGroup:(CPRadioGroup).

For now, we will just create a group into which we will add our radio buttons.  Add the following to AppController:
var radioGroup = [[CPRadioGroup alloc] init];

Creating Our Buttons

Like we did before, we will create radio group buttons with titles.  We will create multiple this time in order to demonstrate the grouping option and see how to extract the selected value.  Add the following code after the radioGroup definition:

var radio1 = [CPRadio radioWithTitle:@"Option 1"], radio2 = [CPRadio radioWithTitle:@"Option 2"], radio3 = [CPRadio radioWithTitle:@"Option 3"];
[radio1 setFrameOrigin:CPPointMake(140,200)];
[radio2 setFrameOrigin:CPPointMake(140,215)];
[radio3 setFrameOrigin:CPPointMake(140,230)];
[radio1 setRadioGroup:radioGroup];
[radio2 setRadioGroup:radioGroup];
[radio3 setRadioGroup:radioGroup];
[contentView addSubview:radio1];
[contentView addSubview:radio2];
[contentView addSubview:radio3];

This creates:

Play with this interface, and you will discover that the three new option radios cannot be selected at the same time.  It is not possible, because when one is activated, it checks to see if it is part of a CPRadioGroup, and that CPRadioGroup cycles through all the other options to deselect them.  This of course does not need to be done by a CPRadioGroup option, we could do it by hand, but why would we want to?

We notice also, that the Radio button created in the parent tutorial is not affected by any of these changes, because it is not part of the CPRadioGroup.  We can change the option choice as many times as we like, and it will never clear the "Radio" choice at the top of the page.

Getting The Selected Value

setAction and setTarget

We do not need to use setAction or setTarget to get the value of a radio button, but here we have an opportunity to make our exploration more simple on ourselves.  We learned about setAction and setTarget methods when we explored CPButtons.  We learned then that setAction and setTarget were methods belonging to all CPControl objects, including CPRadio.

The good news is that the developers of Cappuccino recreated these methods in CPRadioGroup as well.  They behave the same way as we would expect them to behave, and like the CPButton example, we will keep things simple by using the AppController as the target.

Add this code:
[radioGroup setTarget:self];
[radioGroup setAction:@selector(getSelectedOption:)];

Remember to format the method selector properly by including the colon and omitting any quotations.  To ensure that we have complete code, we will also create an empty method at this time by adding the following to AppController:
-(void)getSelectedOption:(id)aRadioGroup
{
   //do something
}
I have named the method attribute "aRadioGroup" to remind us that we have attached this method to a radio group object, and therefore, we expect the calling object to be a radio group.  However, we can also attach this method to other objects as well, and the (id) reminds us that any object can be passed to this function.

Extracting the Selected CPRadio

CPRadioGroup has a function to return the selected item from its contained radio box items.  We are going to use the selectedRadio method to grab the CPRadio object.

Add the following to the getSelectedOption method:
var radioButton = [aRadioGroup selectedRadio];
Our variable radioButton now points to a CPRadio object, and all things going well, it should be the radio object that we have currently selected.  Let's test that theory by extracting the radio button text, and verifying that the radio button has a selected state.  Add the following next:
var title = [radioButton title];
var stateInt = [radioButton state];
alert( title );
alert( stateInt );
The title is a CPString and the state is recorded as a CPInteger, so we can pass them to the UI via a javascript alert function.  Even though seeing the int value of the state will not verify for us that the radio button is selected, because we don't know which integer value yet represents that state, we will at least see that we have made progress on our path of discovery.

Run the application and see what happens when you select an option.

When I run my application, I see the following:
First alert window to appear when application is run.



Second alert window to appear when application is run.
We can probably assume that the integer value of 1 indicates that the button is selected (correlating to the true value that might be used in other languages), but let's investigate further by cycling through all the options to see what values are set when an option is selected.

Add the following to the getSelectedOption method:
var allRadios = [aRadioGroup radios];
for( var i = 0; i < [allRadios count]; i++ ){
     var curRadio = allRadios[i];
     var title = [curRadio title];
     var status = [curRadio state];
     var messageString = "Radio Option (" + title + "):" + status;
     alert( messageString );
}
If you run the application again, you will see that no matter which radio option is selected, that this loop shows us the following values, not necessarily in the same order as depicted here:  1, 0, 0.  This confirms that 1 indicates a selected state, and the 0 indicates a non-selected state.

CPControl defines some constants that are used throughout the CPControl objects: CPOnState = 1, and CPOffState = 0.  This allows us to make our code a little more readable so that we can check a states value with one of the following more readable formats:
var buttonIsSelected = ([button state] == CPOnState);
var buttonIsNotSelected = ([button state] == CPOffState);
Note, if you try putting these lines of code in your application at this point, they won't do anything, because we have not defined the variable "button," and we also have done nothing with the resulting bool values.

No comments:

Post a Comment