SSCircularSlider

Version 1.1

By Ben Haller
Copyright © 2002 Stick Software
All Rights Reserved

The latest version of this documentation may be found here.
SSCircularSlider's home page is located here.


SSCircularSlider is an NSView subclass that acts as a control for choosing both an angle and a distance (a vector, in other words). It is quite configurable, and has a nice Aqua appearance. It provides a circular base, with a knob that may be dragged within the constraints of the base to select values in two dimensions simultaneously. Some basic sets of constraining behavior are built into the class; others may be added by subclassing, or by modifying the class code itself.

SSCircularSlider comes with a palette and an inspector for Interface Builder. Those components are not documented, however; this documentation is only for the SSCircularSlider class itself. The palette and inspector may be used simply by opening the supplied palette in Interface Builder; documentation should not be needed.

SSCircularSlider was written partially for its own utility, and partially as a demonstration of the Aquatint app by Stick Software. It is copyrighted, but it is also open source, licensed under the terms of the Artistic License, which should be included in this distribution. It is provided to the community as a public service. Enjoy!



Class Description


Size

SSCircularSlider works best at either a large (80 by 80 pixels) size or a small (60 by 60 pixels) size. At these sizes, it has a nice Aqua appearance; at other sizes, it falls back on a very rudimentary Quartz appearance that will probably not be satisfactory for most applications.


Values

SSCircularSlider can return and receive values in two forms: angle/distance or xOffset/yOffset.

The angle/distance form is accessed via the -setAngle:andDistance: and -getAngle:andDistance: methods. By default, the angle is in radians, with zero being to the right, and increasing in the counterclockwise direction (the standard mathematical way of describing angles). Using the -setAngleUnits: method, this may be changed to use degrees, with zero at the top and increasing in the clockwise direction (the way many people learned to describe angles in school), with the following enumeration:

typedef enum _SSCircularSliderAngleUnits {
    SSAnglesAreRadians = 0,
    SSAnglesAreDegrees
} SSCircularSliderAngleUnits;

The distance in the angle/distance form ranges from 0.0 at the center to 1.0 at the edge by default, but the maximum distance may be changed with the -setMaximumDistance: method.

The xOffset/yOffset form is accessed via the -setXOffset:andYOffset: and -getXOffset:YOffset: methods. The x offset is positive to the right and negative to the left, the y offset is positive upward and negative downward. These values range from -1.0 to 1.0 by default (constrained such that the total distance from the center is not more than 1.0), but the maximum distance may be changed with the -setMaximumDistance: method.

Some people have requested a third form for values to be vended in: an angle around the disc (0 to 360) with an angle from the center of the disc (0 to 90). This form may be gotten by simply setting the maximum distance to π/2 or 90 (for radians or degrees) with -setMaximumDistance:, and then using the angle/distance form (changing to degrees using -setAngleUnits: if desired). For this reason, I have chosen not to muddy the APIs with a third form at this time.

SSCircularSlider is configured to track the mouse automatically. Normally, the ball will stick to the mouse (as the user would expect a knob to behave). If the option key is pressed, the knob will move more slowly than the mouse, allowing fine-grained adjustments to the control's value. This feature is not obvious, so if it is important to your application, you are encouraged to mention it in the tooltip you provide on your slider, and in other documentation.


Snapping and Ticks

SSCircularSlider provides a number of facilities for contraining the range of legal values the user can select.

To constrain the allowed distance values, the -setAllowsCenterPoint: and -setAllowsNonEdgePositions: methods may be used. These can be used to exclude the exact center of the control from the legal value set, and to exclude all points not at the maximum distance from the legal value set. These choices determine the basic shape of the slider; by default, all points of less than or equal to the maximum distance are allowed, resulting is a solid disc shape.

The -setSnapBehavior: method can be set to contrain angle values none of the time, all of the time, or when the shift key is pressed:

typedef enum _SSCircularSliderSnapBehavior {
    SSSnapNever = 0,
    SSSnapAlways,
    SSSnapOnShift
} SSCircularSliderSnapBehavior;

This constraint works by "snapping" the knob to values that lie at particular angles. The -setNumberOfSnapMarks: method determines how many such angles exist; they are always distributed evenly around the disc, beginning at the right (0 radians). The -setDrawTicksBehavior: can be configured so that snap ticks are displayed around the disc, using the same SSCircularSliderSnapBehavior enumeration above.


Appearance & Behavior

The -setAppearance:method may be used to set the slider's tint to one of a set of enumerated values:

typedef enum _SSCircularSliderAppearance {
    InactiveTintedAppearance = -1,    // for internal use only; use setEnabled:NO to disable the control

    SystemTintedAppearance = 0,
    BlueTintedAppearance,
    GraphiteTintedAppearance,
    QuartzAppearance
} SSCircularSliderAppearance;

As noted in the Size section above, sizes other than 60x60 and 80x80 will be shown using the Quartz appearance regardless of the appearance value set.

Finally, the control may be enabled or disabled using the -setEnabled: method, which changes its appearance as well as its responsiveness to clicks.


Using SSCircularSlider in nibs

SSCircularSlider is a subclass of NSView, not of NSControl. This is mostly of no consequence (apart from meaning it isn't saddled with a huge load of inherited APIs that don't apply to it). However, it does mean that a target/action connection for it cannot be made directly in Interface Builder; such connections must be made in code using -setTarget: and setAction:. I have high hopes that Apple will correct this deficiency in Interface Builder in the future, to allow arbitrary classes to specify that they possess target/action connections.

An Interface Builder palette and inspector is part of the distribution of SSCircularSlider, so if you choose to load that palette in IB, you should be able to configure your circular sliders directly in IB. However, you can also configure them in code, of course; typically, such setup would be done immediately after a nib is loaded, using an outlet connected to the SSCircularSlider in need of configuration. The Circular Slider project, available at Stick Software's web site here, demonstrates the use of SSCircularSlider in a typical nib-based application.


Delegation and subclassing

Since it is not yet clear to what uses SSCircularSlider might be put, no delegate model has been put into this release, and no particular attention has been paid to allowing subclasses to modify the behavior of SSCircular Slider. If you see a need for these features in your application, feel free to contact us with suggestions or code changes. Please note that submitted code changes will be subject to the terms of Circular Slider's copyright notice, in order to allow us to integrate and distribute them in future versions of SSCircularSlider without legal issues arising.


Method Types


    Getting images

        + baseImageWithCenter:nonEdge:big:
        + knobImageWithTint:highlighted:big:

    Creation & Archiving

        - initWithFrame:
        - initWithCoder:
        - encodeWithCoder:

    Appearance & Enabling

        - setAppearance:
        - appearance
        - setEnabled:
        - isEnabled

    Value units

        - setMaximumDistance:
        - maximumDistance
        - setAngleUnits:
        - angleUnits

    Getting and setting values

        - setAngle:andDistance:
        - getAngle:andDistance:
        - setXOffset:andYOffset:
        - getXOffset:andYOffset:

    Value constraints

        - setAllowsCenterPoint:
        - allowsCenterPoint
        - setAllowsNonEdgePositions:
        - allowsNonEdgePositions
        - setSnapBehavior:
        - snapBehavior
        - setNumberOfSnapMarks:
        - numberOfSnapMarks
        - setDrawTicksBehavior:
        - drawTicksBehavior
        - constrainAngle:andDistance:
        - constrainState

    Target action

        - setTarget:
        - target
        - setAction:
        - action
        - setContinuous:
        - isContinuous
        - sendAction:to:
        - sendAction

Class Methods


baseImageWithCenter:nonEdge:big:

+ (NSImage *)baseImageWithCenter:(BOOL)center nonEdge:(BOOL)nonEdge big:(BOOL)big

Returns a "base" image that conforms to the flags passed in. The flag center determines whether the center position is legal or not, nonEdge whether the interior positions are legal or not, and big whether the image fetched should be the 80x80 or the 60x60 size. The image returned is suitable for drawing as the base of a circular slider. Each possible image is loaded separately and lazily from the app bundle, and cached permanently in memory.

See Also: + knobImageWithTint:highlighted:big:



knobImageWithTint:highlighted:big:

+ (NSImage *)knobImageWithTint:(SSCircularSliderAppearance)tint highlighted:(BOOL)highlighted big:(BOOL)big

Returns a "knob" image that conforms to the flags passed in. The enumerated value tint determines whether the image returned is blue, graphite or inactive; it should have only the values BlueTintedAppearance, GraphiteTintedAppearance or InactiveTintedAppearance. The flag highlighted determines whether a darkened, highlighted knob image is returned. The flag big determines which size of knob is returned; normally a big knob is used with an 80x80 circular slider, a small knob with a 60x60 one. The image returned is suitable for drawing as the knob of a circular slider. Each possible image is loaded separately and lazily from the app bundle, and cached permanently in memory.

See Also: + baseImageWithCenter:nonEdge:big:



Instance Methods


action

- (SEL)action

Returns the selector that would be sent as an action if the slider's value were changed by the user. There is no default action.

See Also: - setAction:, - target, - sendAction



allowsCenterPoint

- (BOOL)allowsCenterPoint

Returns YES if the center point (0.0, 0.0) of the slider is allowed as a value. Note that positions arbitrarily close to (0.0, 0.0), within the precision limits of the float type, will still be legal.

See Also: - setAllowsCenterPoint:, - allowsNonEdgePositions, - constrainAngle:andDistance:



allowsNonEdgePositions

- (BOOL)allowsNonEdgePositions

Returns YES if interior points (distance greater than 0.0 and less than the maximum distance) are allowed as values.

See Also: - setAllowsNonEdgePositions:, - allowsCenterPoint, - constrainAngle:andDistance:



angleUnits

- (SSCircularSliderAngleUnits)angleUnits

Returns SSAnglesAreRadians if angles given and taken by the receiver will be in radians, with zero at the right and increasing counterclockwise. Returns SSAnglesAreDegrees if angles given and taken by the receiver will be in degrees, with zero at the top and increasing clockwise.

See Also: - setAngleUnits:, - getAngle:andDistance:, - setAngle:andDistance:



appearance

- (SSCircularSliderAppearance)appearance

Returns the enumerated value of the slider's appearance setting. This should be one of SystemTintedAppearance, BlueTintedAppearance, GraphiteTintedAppearance, or QuartzAppearance.

See Also: - setAppearance:



constrainAngle:andDistance:

- (void)constrainAngle:(float *)angle andDistance:(float *)distance

Constrains the angle and distance passed in, based upon the slider's current constraints (maximum distance, allowing the center point, allowing interior points, and angle snapping). The constrained values are returned in the pointers passed in; the return value is YES if the constrained values differ from the values passed in, NO if the values passed in were not modified. This method is called whenever the control's value is changed, including during the tracking loop resulting from a mouse-down in the slider.

Because this method is part of SSCircularSlider's internal API, and is not really intended to be called by users of the class, the angle passed in (and out) should always be in radians, since that is the internal representation for angles.

See Also: - constrainState, - allowsCenterPoint, - allowsNonEdgePositions, - maximumDistance, - snapBehavior



constrainState

- (SSCircularSliderAppearance)constrainState

Constrains the current state of the slider by calling - constrainAngle:andDistance:. If the contrained values differ from the unconstrained values, they are set back into the slider's state, setNeedsDisplay:YES is called, and YES is returned. If the constrained values are the same as the unconstrained values, NO is returned. This method is called whenever the constraints of a slider change, such that the slider's current value may no longer be valid.

See Also: - constrainAngle:andDistance:, - allowsCenterPoint, - allowsNonEdgePositions, - maximumDistance, - snapBehavior



drawTicksBehavior

- (SSCircularSliderSnapBehavior)drawTicksBehavior

Returns the tick drawing behavior used by the receiver. Tick drawing can occur never, always, or when shift is down, according to the values SSSnapNever, SSSnapAlways, and SSSnapOnShift.

See Also: - setDrawTicksBehavior:, - numberOfSnapMarks, - snapBehavior



encodeWithCoder:

- (void)encodeWithCoder:(NSCoder *)coder

This calls -[NSView encodeWithCoder:], and then archives further values defining the receiver into the NSCoder's data stream. Typically, this is called by Interface Builder when archiving a nib; it would be unusual to call it yourself.

See Also: - initWithCoder:



initWithFrame:

- (id)initWithFrame:(NSRect)frame

A designated initializer for SSCircularSlider. It calls -[NSView initWithFrame:] and then sets up default values for the slider's attributes. By default, sliders are enabled, allow the interior and center positions, have a maximum distance of 1.0, an initial value of angle 0.0 and distance 1.0, and use the tint SSSystemTintedAppearance. The action is unset, but their target is FirstResponder, and they are set to fire their action continuously during tracking.



initWithCoder:

- (id)initWithCoder:(NSCoder *)coder

A designated initializer for SSCircularSlider. It calls -[NSView initWithCoder:] and then decodes the further values archived in the coder's data stream. Typically, this initializer is called automatically when instantiating SSCircularSlider from a nib; it would be unusual to call it yourself.

See Also: - encodeWithCoder:



isContinuous

- (BOOL)isContinuous

Returns YES if the slider's action will be sent to the target continuously as the knob is dragged by the user. If NO, the slider's action is sent only when tracking is completed.

See Also: - setContinuous:, - sendAction



isEnabled

- (BOOL)isEnabled

Returns YES if the control's value can be changed by the user. Disabled sliders are drawn with a dimmed appearance.

See Also: - setEnabled:



getAngle:andDistance:

- (void)getAngle:(float *)angle andDistance:(float *)distance

Returns the slider's current value as an angle and distance from the center. The angle is returned in the units specified by - angleUnits.

See Also: - setAngle:andDistance:, - angleUnits, - getXOffset:andYOffset:



getXOffset:andYOffset:

- (void)getXOffset:(float *)xOffset andYOffset:(float *)yOffset

Returns the slider's current value as an x and y offset from the center.

See Also: - setXOffset:andYOffset:, - getAngle:andDistance:



maximumDistance

- (float)maximumDistance

Returns the maximum distance from the center allowed for values. Values at the maximum distance will always be found around the rim of the slider's base; the slider's "scale" is adjusted to ensure that this remains true at all times.

See Also: - setMaximumDistance:



numberOfSnapMarks

- (int)numberOfSnapMarks

Returns the number of discrete angles that the current angle-snapping behavior (available from - snapBehavior) will use. The first angle snapped to is always at radian 0.0 (to the right, in other words); the other angles are distributed evenly around the circumference of the slider.

See Also: - setNumberOfSnapMarks:, - snapBehavior, - drawTicksBehavior



sendAction

- (BOOL)sendAction

Sends the slider's action to the slider's control. This method does not typically need to be called; it is called as appropriate by SSCircularSlider's tracking loop automatically. However, sendAction is not normally called when the control's value is changed programmatically, so this method may be called to force the action to be sent in these cases if desired.

See Also: - sendAction:to:, - target, - action, - isContinuous



sendAction:to:

- (BOOL)sendAction:(SEL)theAction to:(id)theTarget

Sends theAction to theTarget with the slider as sender. This method does not typically need to be called.

See Also: - sendAction



setAction:

- (void)setAction:(SEL)aSelector

Sets the action that will be sent when the user changes the value of the slider. There is no default action.

See Also: - action, - setTarget:, - sendAction



setAllowsCenterPoint:

- (void)setAllowsCenterPoint:(BOOL)flag

Sets whether the center point (0.0, 0.0) is allowed as a value. Note that positions arbitrarily close to (0.0, 0.0), within the precision limits of the float type, will still be legal. To exclude a larger set of values surrounding (0.0, 0.0), an override of - constrainAngle:andDistance: would be necessary.

See Also: - allowsCenterPoint, - setAllowsNonEdgePositions:



setAllowsNonEdgePositions:

- (void)setAllowsNonEdgePositions:(BOOL)flag

Sets whether interior positions with distance greater than 0.0 and less than the maximum distance are allowed as values. To implement a more complex definition of legal versus illegal values, - constrainAngle:andDistance: may be overridden.

See Also: - allowsNonEdgePositions, - setAllowsCenterPoint:



setAngle:andDistance:

- (void)setAngle:(float)angle andDistance:(float)distance

Sets the current value of the slider to the angle and distance given. The value is constrained by - constrainAngle:andDistance: before it is set on the slider. The angle passed in is expected to be in the units specified by - setAngleUnits:.

See Also: - getAngle:andDistance:, - setAngleUnits:, - setXOffset:andYOffset:, - constrainAngle:andDistance:



setAngleUnits:

- (void)setAngleUnits:(SSCircularSliderAngleUnits)units

Sets the units used to represent degrees in the API. Pass in SSAnglesAreRadians if you want angles given and taken by the receiver to be in radians, with zero at the right and increasing counterclockwise. Pass in SSAnglesAreDegrees if you want angles given and taken by the receiver to be in degrees, with zero at the top and increasing clockwise.

See Also: - angleUnits, - setAngle:andDistance:, - getAngle:andDistance:



setAppearance:

- (void)setAppearance:(SSCircularSliderAppearance)appearance

Sets the slider's appearance to one of SystemTintedAppearance, BlueTintedAppearance, GraphiteTintedAppearance, or QuartzAppearance. Note that if the slider's frame is a size other than 80x80 or 60x60 pixels, QuartzAppearance will be used regardless of this setting.

See Also: - appearance



setContinuous:

- (void)setContinuous:(BOOL)flag

Sets whether the slider's action is sent continuously as the user drags the knob, or only at the completion of tracking.

See Also: - isContinuous, - sendAction



setDrawTicksBehavior:

- (void)setDrawTicksBehavior:(SSCircularSliderSnapBehavior)behavior

Sets the tick drawing behavior used by the receiver. Ticks can be drawn never, always, or when shift is down, according to the values SSSnapNever, SSSnapAlways, and SSSnapOnShift. The ticks are drawn at angles determined by - setNumberOfSnapMarks:. The behavior of angle snapping can be controlled separately using - setSnapBehavior:.

See Also: - drawTicksBehavior, - setNumberOfSnapMarks:, - setSnapBehavior:



setEnabled:

- (void)setEnabled:(BOOL)flag

Sets whether the slider's value can be changed by the user. Disabled sliders are drawn with a dimmed appearance.

See Also: - isEnabled



setMaximumDistance:

- (void)setMaximumDistance:(float)maxDistance

Sets the maximum distance allowed for values. This does not change the appearance of the slider, but rather, the values assigned to the various points the knob can be dragged. Values at the maximum distance will always be found around the rim of the slider's base; the slider's "scale" is adjusted to ensure that this remains true at all times.

See Also: - maximumDistance



setNumberOfSnapMarks:

- (void)setNumberOfSnapMarks:(int)count

Sets the number of discrete angles that the angle-snapping behavior set by - setSnapBehavior: will use. The first angle snapped to is always at radian 0.0 (to the right, in other words); the other angles are distributed evenly around the circumference of the slider.

See Also: - numberOfSnapMarks, - setSnapBehavior:, - setDrawTicksBehavior:



setSnapBehavior:

- (void)setSnapBehavior:(SSCircularSliderSnapBehavior)behavior

Sets the angle snap behavior used by the receiver. Snapping can occur never, always, or when shift is down, according to the values SSSnapNever, SSSnapAlways, and SSSnapOnShift. The snapping happens at angles determined by - setNumberOfSnapMarks:. The behavior of tick drawing can be controlled separately using - setDrawTicksBehavior:.

See Also: - snapBehavior, - setNumberOfSnapMarks:, - setDrawTicksBehavior:



setTarget:

- (void)setTarget:(id)anObject

Sets the target to which actions will be sent when the user changes the value of the control. The FirstResponder is the default target, represented by a target value of nil.

See Also: - target, - setAction:, - sendAction



setXOffset:andYOffset:

- (void)setXOffset:(float)xOffset andYOffset:(float)yOffset

Sets the current value of the slider to the x and y offsets given. The value is converted to an angle and distance, and then constrained by - constrainAngle:andDistance:, before it is set on the slider.

See Also: - getXOffset:andYOffset:, - setAngle:andDistance:, - constrainAngle:andDistance:



snapBehavior

- (SSCircularSliderSnapBehavior)snapBehavior

Returns the angle snap behavior used by the receiver. Snapping can occur never, always, or when shift is down, according to the values SSSnapNever, SSSnapAlways, and SSSnapOnShift.

See Also: - setSnapBehavior:, - numberOfSnapMarks, - drawTicksBehavior



target

- (id)target

Returns the target to which actions will be sent when the user changes the value of the control. The FirstResponder is the default target, represented by a target value of nil.

See Also: - setTarget:, - action, - sendAction




Copyright © 2002 Stick Software. All Rights Reserved.
Last revised August 29 2002.