BAUIStarSlider Source Package

We got some requests from people asking if they can use our BAUIStarSlider code in their applications. We are happy to provide you with the source code and to let you use it in your applications. We only ask that you don’t redistribute it as your own code and if anyone inquires to point them our way. If you do use the star slider code, we would appreciate it if you could give us some credit and link to our site. Other than that feel free to use it however you want and let us know what applications you’ve used it in.

Here is the zip file: BAUIStarSlider Package

Tags: , ,

Custom Star Ratings Slider Using Quartz2D Part 2

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.

Tags: , ,

Star Rating Slider

I remember looking at Apple’s iTunes rating slider and thinking to myself, that will be perfect to integrate into BeefyBoard: The iPhone Soundboard for rating soundboards and skins alike.  Imagine my dismay upon combing through the SDK and coming up empty handed.  Not to be deterred though, I managed to recreate something similar to apple’s slider using a couple images and the base UISlider implementation that Apple provides.

sliderblog1

Apple’s UISlider component is made up of two images one for the left, which I like to call the filled side, and another image for the right, or the empty side.  Apple deals with these images by using some stretchable images with caps and this lets them resize the slider to their heart’s content.  We however do not really have that luxury.

Gotcha #1: The left and right images need to be the same size and you need to account for the frame padding that Apple adds in as part of the component.  On the slider that I worked with, my two images were 250px wide but my slider control had to be 254px wide in order to display properly.  You will notice if you start playing with the size that it will mess up your image if you get this wrong.

Gotcha #2: The thumb image that you choose will affect the size and the position of the two images and where they meet.  Even though you might have figured out how to get the images to line up, if you are using a thumb control image the UISlider will adjust the size of the image to take this into account.  For instance, if you make your thumb image 50px then 50 px will be added to the width of the control to take into account the width of the thumb on either side of the slider.

Now that we have talked about all the sizing problems we should be great right?  Not yet, we still need to worry about the way that you control the slider.  Between the 2.1 and 2.2 releases of the iPhone SDK the behaviour has changed for the touch sensitivity of the UISlider.  In 2.1 you used to be able to touch anywhere on the slider and the slider would move to that position.  In 2.2 some people complained that they were accidentally changing settings and thus it was removed. So…

Gotcha #3: If you do not add a thumb image in your slider you will not have any hit area to click on in order to adjust the value of the slider.  You could add a transparent thumb icon if you wanted to provide a hit area but not have the graphic, but remember gotcha #2 above, the UISlider will adjust the size of the slider and you will need to account for that.

To solve this problem, I went in and enabled touch support for a UISlider subclass that I created.  I added touch support for the slider being pressed and again for a finger being moved on the slider.  One important thing that I almost missed was to fire the value changed action on the UISlider so that it would tell any registered selectors that the rating value had changed.

I hope this helps some people out there to avoid some problems and if you have any questions please feel free to leave a comment.  I will try to answer the best I can.

Tags: , , , ,