By Andrew Stone of Stone Design
May 1990
SliderDualActing and its cell class, SliderCellFine, combine a special ªdual-acting slider, a text field, and two arrow buttons into one composite object. This allows the end-user multiple input techniques while abstracting theclient or application user of the object away from the details of coordinating the various Appkit components. Thespecial slider solves some of the problems of providing fine control over a large range of values in a small space.This project illustrates the power gained both by subclassing Appkit objects and combining objects.

Component objects make programming easier:
The end-user gets multiple input methods: a slider, a textfield, and a pair of buttons which decrement or increment the slider, while the program only deals with one object. All of the validation and synchronization methods are in one place instead of distributed throughout source files. Programs often have lines like these in read and other resetting methods:
[numberField setIntValue:number];

if (number>    [numberSlider maxValue]&&okToExceedMaximum)

            [numberSlider setMaxValue:number];

[numberSlider setIntValue:number];
All of the coordinating code can be replaced by the last line. The SliderDualActing handles the rest.

•We want to continually update the textfield as we drag the slider (and SliderDualActing handles this without any programmer intervention), but we also want to notify another target either continually or on mouseUp. The second case is more usual, as continual notification may take too much processing time. Therefore, our composite object has an UpTarget and an UpAction.

•Users should not be stopped from entering values higher than maxValue or lower than minValue, if it makes sense to the application. For example, in TextArt, the fontSizeSlider has a default value of 140 points. However, if users want a higher value, they simply type in a value into the textPal, and if the SliderDualActing BOOL instance variable allowHigher is YES, the slider resets its maximum value. A further enhancement to this class would be methods to write the user’s maximum and minimum values to the NX_Defaults mechanism to re-store on the next launch.

•Sometimes, the user justs wants to ªcrawl along the slider’s values. I have added a matrix of two buttons which increment or decrement the slider by altStep. They connect to the sliders’ action method incrementDecrement:.

• Component objects can provide a convenient method for implementing Undo. The SliderDualActing knows its last value and can respond to an undo method. Alternatively, you can specify an undo target and a tag telling the target what needs to be undone. The default is for the slider to handle undo itself. One simple strategy for a single-level undo is to have a global undo object, which stores the id of the last control set. Undo from the menu tells this undo object to send the undo method to the control that last set it. My undo object is accessable via NX-App, and looks like this:

@interface Undo:Object


    BOOL hasUndo;

    id lastClass;

- setLastClass:anID;

- undo:sender;

Special sliders provide several additional user
interface features:

• Sliders with a large range can cause problems: If the slider isn’t very long, the resolution (how much the value changes when the slider is dragged one pixel) can become unacceptably large. On the other hand, screen space is often in short supply, making it hard to use the long sliders needed to obtain a fine resolution. SliderDualActing and SliderCellFine provide methods to change the slider’s value by specifiable small amounts by checking if the Alternate or other meta keys are down while the slider is being dragged. Currently, pressing the Alternate key causes the slider to change by the altStep instance variable and adding the Shift key halves this amount.

• Looking at float values that have insignificant digits is noisy and very unSteveLike.. Sometimes, however, a user wants to specify a value to a high degree of accuracy. Our composite object provides a mechanism to dynamically change the TextField textPal’s floating point format. Normally the user will not be bothered with the floating point values, but dragging the slider while pressing the Alternate and Shift keys puts the SliderDualActing into decimal state (as well as changing the value by a prespecified small amount). This allows precision while preserving aesthetics.

• The user can reset the slider to its various defaults (maximum, minimum, number of decimal places displayed) while using the program by clicking the slider while pressing the Command key.

Using SliderDualActing, a step by step guide
in Interface Builder:

The first time the object is used, perform steps 1 thru 13; thereafter only step 13 is necessary:

0] Assume that you have a class with an instance variable named sliderDA

1] Copy SliderDualActing.[hm] and SliderCellFine.[hm] to your project directory.

2] Bring up the Class Window (Command-5) and the Class Attributes Inspector (Command-1)

3] Make a subclass of slider:
    a] Traverse the Class hierarchy to Object->Responder->View->Control->Slider
    b] Select SubClass from the Operations pull-down menu in the Class Window
    c] Rename the subclass to SliderDualActing
    d] Select Parse from the Operations pull-down menu and answer OK to the Add

        SliderDualActing to Project dialogue box

4] Create a new custom view by dragging one from the Palette Window

5] Make it a SliderDualActing by bringing up Inspector Attributes Window (Command-1) and clicking on
the SliderDualActing class name

6] Size the new slider: Bring up Inspector Size Window (Command-4) and type in a width of 16. if it’s to be a vertical slider, or a height of 16 if it’s a horizontal slider.

7] Drag a textField from the Palette Window Object kept together in box This is what a SliderDualActing looks like in Interface Builder.

8] Drag a button from the Palette Window. Make a matrix of two buttons by dragging while pressing the Alt key. In the Inspector Attributes window, be sure to check Cells Tag = Position. Add arrow icons to these buttons.

9] Select the three objects and box them by typing Command-g. This allows you to copy and Paste the control while retaining the connections among the three objects.

10] Connect the textPal outlet of sliderDualActing to the textfield.

11] Connect the action of the textfield to the SliderDualActing method: sendTextAction:

12] Connect the matrix’ target to the SliderDualActing method incrementDecrement:

13] Connect your control object’s sliderDA outlet to the sliderDualActing view. Now, for other instances of SliderDualActing, you can copy and paste this box, and only need to set your application’s control object to the slider itself.

In your code, in the ªappDidInit method sent to the App add some initialization code:

You need to set the slider’s UpTarget and UpAction and its various default values. An excellent place for this is after the Application receieves an AppDidInit: message. For example
/* The app has an outlet named "sliderDA" in this example */

- appDidInit:sender


    [[[[sliderDA setUpTarget:self action:@selector(setAngle:)]

        setMax:360. allowHigher:NO min:-360. allowLower:NO]

        setAltStep:1. whole:YES default:0]

        setFormat:NO left:1 right:3]; // formats text pal

        /* other initialization code here */

    return self;

•Problem: IB has no inspector for subclasses of known objects in V1.0. This means you lose the ability to specify various defaults in IB.

Fix: Call the initialization routines illustrated above and
documented in SliderDualActing.m.

•Problem: If the value of altStep is large compared to the range of the slider, drawing update anomalies occur. In-stead of adding resolution, you subtract it. SliderDualActing should have more error checking for badparameters.

The archiving methods have not been tested since I read mine in from a nib file.

SliderDualActingDistribution (available in
NeXT archives everywhere):


copyright 1990 Andrew C. Stone.
All Rights Reserved.

Stone Design's Create(tm)
2005-06-30 22:30:56 -0500