In Part 1 we talked about creating a custom UIView class that draws fillable stars. We’ve edited the code in Part 1 to fix some errors that were discovered when implementing the slider, mainly that the scaling of the coordinates should only happen once, (upon initialization) and not in the drawRect method.

In this part we will talk about using this fillable star to create a custom UISlider. Here is the interface definition for the BAUIStarSlider.

typedef enum
{
ApproximationModeNone,
ApproximationModeWhole,
ApproximationModeHalf,
ApproximationModeQuarter

} SliderApproximationMode;

@interface BAUIStarSlider : UIControl {

NSMutableArray * starArray;
int numStars;
SliderApproximationMode approxMode;
float value;

}
@property(nonatomic) SliderApproximationMode approxMode;
@property(nonatomic) float value;

-(id)initWithFrame:(CGRect)frame andStars:(int)inNumStars;

@end

As you can see the BAUIStarSlider is a subclass of UIControl which will allow us to make use of the event dispatching methods found in UIControl. The implementation of the custom slider is pretty straight forward. There are only a few methods that need to be implemented, the first being the initialization method:

-(id)initWithFrame:(CGRect)frame andStars:(int)inNumStars
{
if (self = [super initWithFrame:frame])
{
numStars = inNumStars;
starArray = [[NSMutableArray alloc] initWithCapacity:numStars];
approxMode = ApproximationModeWhole; //default approximation mode

float width = frame.size.width/numStars;
float height = frame.size.height;

for(int i=0; i < numStars; i++)
{

BAFillableStar * star = [[BAFillableStar alloc] initWithFrame:CGRectMake(0+(i*width), 0, width, height)];
[starArray addObject:star];
[self addSubview:star];
[star release];

}

}

return self;

}

The next two methods deal with setting the value on the slider using touch. When a user touches a point on the star, we want to fill the stars of the slider up to that point. We also want to allow the user to slide their finger along the slider and when they do this we want to fill or clear the stars along the slider appropriately.

– (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch* touch = [touches anyObject];
CGPoint touchPoint = [touch locationInView:self];

//make sure the points are within our bounds
if(touchPoint.x < 0 )
{

touchPoint.x = 0;

}
if(touchPoint.x > self.frame.size.width)
{

touchPoint.x = self.frame.size.width;

}

value = touchPoint.x / (self.frame.size.width/numStars);
[self fillStars];

}
– (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{

//just call the touchesBegan method
[self touchesBegan:touches withEvent:event];

}

The final two methods to implement do the actual filling of the stars based on the value that was set using the touch methods. The implementation of setFillPercent in the BAFillableStar class calls [self setNeedsDisplay] so that when the fill percent is set, the star will automatically redraw itself. We want to implement our own setValue method so that when the value of the slider is initially set the correct number of stars get filled properly. The getApproximatedPart method uses the approximation mode to round the part value, so if the approximation mode is none, no rounding happens, if the mode is half, then it will round it to the nearest half value, etc…

-(void)fillStars{
float whole = floor(value);
float part = value – whole;
part = [self getApproximatedPart:part];

//dispatch a value changed event
value = whole + part; //since we approximated
[self sendActionsForControlEvents:UIControlEventValueChanged];

int i = 0;
for(BAFillableStar * star in starArray)
{

if( i < whole)
{
star.fillPercent = 1;

}
else if ( part > 0 )
{

star.fillPercent = part;
part = 0;

}
else
{

star.fillPercent = 0;

}
i++;

}

}
-(void)setValue:(float)inValue
{

value = inValue;
[self fillStars];

}

That’s it for this tutorial. Hope you found it informative. If you have any questions ask in the comments section and we will try to answer them as best we can.