
I had some spare time earlier this week and took the opportunity to get to know Core Graphics. Its been a real treat as CG offers a lot of power but it takes some getting used to.
I’d been thinking for some time that a star rating widget for iPhone apps might be useful — so that became the target implementation of my CG journey. At right is the FiveStar widget in action. What you can’t tell from the image is that using the slider will change the value and display of the widget. Using the slider allows the user to select fractional stars.
The user can also use a finger swipe to change the displayed value — in this mode, only whole stars can be ’selected’.
I set out to create this widget without the use of any image files — for a couple reasons:
- I wanted to be able to add other ’shapes’ without having to create new graphics in PS everytime I wanted a new “shape” to use for my stars (i.e. circles, squares, palm trees)
- I wanted to see if CG was fast enough to do the job (I’m not sure what that exactly means other than “doesn’t interfere with a pleasant user experience”)
- I wanted to have the ability to display ‘fractional’ stars — using an image based solution might have required that I create several fractional images.
Initially, I had set out to only display stars either filled with one of two solid colors — one to represent a ‘used’ star and the other to represent an ‘unused’ star. That is, if the widget were to be displaying a rating of three stars, I wanted to see the first three (from left to right) filled with one color and the remaining two filled with another color.
This turns out not to be terribly difficult in CG — simply draw a path in a graphics context and then stroke and fill the path. Once you have a stroked and filled path, you create a CGImageRef from your context and from the CGImageRef you can create the familiar UIImage. (I should note that while drawing paths is simple in CG, generating the path my not be as easy — I had to reach way back into high school geometry to come up with a nifty algorithm to create my star path.
Creating fractional stars turns out to not be too difficult either — I simply created a filled star, clipped it with a rectangle of the appropriate size to capture the desired “fraction” of the star and then overlayed that clipped image onto the image of an unfilled star.
Once I (kind of) had the hang of that, I ventured into creating reflections. I got a huge head start on this part by examining the code in Apple’s TheElements sample project. The net of creating the effect is to invert the image to be reflected, clip the part of the image to show in the reflection and then apply a gradient (gray scale only!).
Finally, I ventured into creating a colored gradient for the stars — turns out to be pretty straight forward as well. Using the original path for the star to clip the CGContext and then apply a gradient to the context. The gradient will only appear in the clipped portion of the context. Next, add the path back in and stroke it (don’t fill it — you’ll lose the gradient!)
I packaged the whole FiveStars widget up into a static library so that I can drop it into various projects. The project I’ve linked to below was used to create the screenshot above. Note that there are two different instantiations of the widget — the large one uses a gradient fill and has a reflection. The instantiation used in the table is a simple fill with no gradient and no reflection — and there is no user interaction in the table — they are ‘read only’.
Here’s a quick video of a FiveStars demo to give you a better idea of what’s going on, and what the widget can do.
I’d love to hear your thoughts, please leave a comment!