问题
I'm trying to adapt an example provided by Apple in order to programmatically draw stars in line, the code is the following:
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(context, aSize);
for (NSUInteger i=0; i<stars; i++)
{
CGContextSetFillColorWithColor(context, aColor);
CGContextSetStrokeColorWithColor(context, aColor);
float w = item.size.width;
double r = w / 2;
double theta = 2 * M_PI * (2.0 / 5.0); // 144 degrees
CGContextMoveToPoint(context, 0, r);
for (NSUInteger k=1; k<5; k++)
{
float x = r * sin(k * theta);
float y = r * cos(k * theta);
CGContextAddLineToPoint(context, x, y);
}
CGContextClosePath(context);
CGContextFillPath(context);
}
The code above draws a perfect star, but is 1. displayed upside down 2. is black and without border. What I want to achive is to draw many stars on the same line and with the given style. I understand that I'm actually drawing the same path 5 times in the same position and that I have somehow to flip the context vertically, but after several tests I gave up! (I lack the necessary math and geometry skills :P)... could you please help me?
UPDATE:
Ok, thanks to CocoaFu, this is my refactored and working draw utility:
- (void)drawStars:(NSUInteger)count inContext:(CGContextRef)context;
{
// constants
const float w = self.itemSize.width;
const float r = w/2;
const double theta = 2 * M_PI * (2.0 / 5.0);
const float flip = -1.0f; // flip vertically (default star representation)
// drawing center for the star
float xCenter = r;
for (NSUInteger i=0; i<count; i++)
{
// get star style based on the index
CGContextSetFillColorWithColor(context, [self fillColorForItemAtIndex:i]);
CGContextSetStrokeColorWithColor(context, [self strokeColorForItemAtIndex:i]);
// update position
CGContextMoveToPoint(context, xCenter, r * flip + r);
// draw the necessary star lines
for (NSUInteger k=1; k<5; k++)
{
float x = r * sin(k * theta);
float y = r * cos(k * theta);
CGContextAddLineToPoint(context, x + xCenter, y * flip + r);
}
// update horizontal center for the next star
xCenter += w + self.itemMargin;
// draw current star
CGContextClosePath(context);
CGContextFillPath(context);
CGContextStrokePath(context);
}
}
回答1:
Here is code that will draw 3 stars in a horizontal line, it's is not pretty but it may help:
-(void)drawRect:(CGRect)rect
{
int aSize = 100.0;
const CGFloat color[4] = { 0.0, 0.0, 1.0, 1.0 }; // Blue
CGColorRef aColor = CGColorCreate(CGColorSpaceCreateDeviceRGB(), color);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(context, aSize);
CGFloat xCenter = 100.0;
CGFloat yCenter = 100.0;
float w = 100.0;
double r = w / 2.0;
float flip = -1.0;
for (NSUInteger i=0; i<3; i++)
{
CGContextSetFillColorWithColor(context, aColor);
CGContextSetStrokeColorWithColor(context, aColor);
double theta = 2.0 * M_PI * (2.0 / 5.0); // 144 degrees
CGContextMoveToPoint(context, xCenter, r*flip+yCenter);
for (NSUInteger k=1; k<5; k++)
{
float x = r * sin(k * theta);
float y = r * cos(k * theta);
CGContextAddLineToPoint(context, x+xCenter, y*flip+yCenter);
}
xCenter += 150.0;
}
CGContextClosePath(context);
CGContextFillPath(context);
}
回答2:
Here's an algorithm to implement what buddhabrot implied:
- (void)drawStarInContext:(CGContextRef)context withNumberOfPoints:(NSInteger)points center:(CGPoint)center innerRadius:(CGFloat)innerRadius outerRadius:(CGFloat)outerRadius fillColor:(UIColor *)fill strokeColor:(UIColor *)stroke strokeWidth:(CGFloat)strokeWidth {
CGFloat arcPerPoint = 2.0f * M_PI / points;
CGFloat theta = M_PI / 2.0f;
// Move to starting point (tip at 90 degrees on outside of star)
CGPoint pt = CGPointMake(center.x - (outerRadius * cosf(theta)), center.y - (outerRadius * sinf(theta)));
CGContextMoveToPoint(context, pt.x, pt.y);
for (int i = 0; i < points; i = i + 1) {
// Calculate next inner point (moving clockwise), accounting for crossing of 0 degrees
theta = theta - (arcPerPoint / 2.0f);
if (theta < 0.0f) {
theta = theta + (2 * M_PI);
}
pt = CGPointMake(center.x - (innerRadius * cosf(theta)), center.y - (innerRadius * sinf(theta)));
CGContextAddLineToPoint(context, pt.x, pt.y);
// Calculate next outer point (moving clockwise), accounting for crossing of 0 degrees
theta = theta - (arcPerPoint / 2.0f);
if (theta < 0.0f) {
theta = theta + (2 * M_PI);
}
pt = CGPointMake(center.x - (outerRadius * cosf(theta)), center.y - (outerRadius * sinf(theta)));
CGContextAddLineToPoint(context, pt.x, pt.y);
}
CGContextClosePath(context);
CGContextSetLineWidth(context, strokeWidth);
[fill setFill];
[stroke setStroke];
CGContextDrawPath(context, kCGPathFillStroke);
}
Works for me for most basic stars. Tested from 2 points (makes a good diamond!) up to 9 stars.
If you want a star with point down, change the subtraction to addition.
To draw multiples, create a loop and call this method multiple times, passing a new center each time. That should line them up nicely!
回答3:
I prefer using a CAShaperLayer
to implementing drawRect
, as it can then be animated.
Here's a function that will create a path in the shape of a 5 point star:
func createStarPath(size: CGSize) -> CGPath {
let numberOfPoints: CGFloat = 5
let starRatio: CGFloat = 0.5
let steps: CGFloat = numberOfPoints * 2
let outerRadius: CGFloat = min(size.height, size.width) / 2
let innerRadius: CGFloat = outerRadius * starRatio
let stepAngle = CGFloat(2) * CGFloat(M_PI) / CGFloat(steps)
let center = CGPoint(x: size.width / 2, y: size.height / 2)
let path = CGPathCreateMutable()
for i in 0..<Int(steps) {
let radius = i % 2 == 0 ? outerRadius : innerRadius
let angle = CGFloat(i) * stepAngle - CGFloat(M_PI_2)
let x = radius * cos(angle) + center.x
let y = radius * sin(angle) + center.y
if i == 0 {
CGPathMoveToPoint(path, nil, x, y)
}
else {
CGPathAddLineToPoint(path, nil, x, y)
}
}
CGPathCloseSubpath(path)
return path
}
It can then be used with a CAShapeLayer like so:
let layer = CAShapeLayer()
layer.path = createStarPath(CGSize(width: 100, height: 100))
layer.lineWidth = 1
layer.strokeColor = UIColor.blackColor().CGColor
layer.fillColor = UIColor.yellowColor().CGColor
layer.fillRule = kCAFillRuleNonZero
来源:https://stackoverflow.com/questions/8445786/how-to-draw-stars-using-quartz-core