How would I animate a circle on the iPhone so that the arc starts at "0 degrees" and ends at "360 degrees"?
Advance Thanks,
Sat
You need to read the Quartz 2D Programming Guide's section on arcs. (I am assuming you are creating an app with the Cocoa Touch API, not a web app.) You also need to know how to set up a custom animation. You will have to create a custom UIView or CALayer to do the drawing, and create a property (arc degree) that can be animated with a CAAnimation object. Alternatively, you can control the animation using an NSTimer instead. You pretty much have to have a grasp of these classes (and others) to pull this off.
Here you can find a great sample code about circle animation:
http://www.iphonedevsdk.com/forum/iphone-sdk-development/24152-draw-animated-circle-iphone-using-core-graphics.html
you should read the documentation that Felixyz provided and if you want an example of how to animate the circle have a look over the MBProgressHUD at this link link text. The loader has two modes one with a UIViewActivityIndicator and a progress indicator (a filling circle that it is animated from 0 to 360 degress) i think the last mode is what you want.
the fallowing code is from copy/paste from that implementation that animates the circle:
- (void)drawRect:(CGRect)rect {
CGRect allRect = self.bounds;
CGRect circleRect = CGRectMake(allRect.origin.x + 2, allRect.origin.y + 2,
allRect.size.width - 4, allRect.size.height - 4);
CGContextRef context = UIGraphicsGetCurrentContext();
// Draw background
CGContextSetRGBStrokeColor(context, 1.0, 1.0, 1.0, 1.0); // white
CGContextSetRGBFillColor(context, 1.0, 1.0, 1.0, 0.1); // translucent white
CGContextSetLineWidth(context, 2.0);
CGContextFillEllipseInRect(context, circleRect);
CGContextStrokeEllipseInRect(context, circleRect);
// Draw progress
float x = (allRect.size.width / 2);
float y = (allRect.size.height / 2);
CGContextSetRGBFillColor(context, 1.0, 1.0, 1.0, 1.0); // white
CGContextMoveToPoint(context, x, y);
CGContextAddArc(context, x, y, (allRect.size.width - 4) / 2, -(PI / 2),
(self.progress * 2 * PI) - PI / 2, 0);
CGContextClosePath(context); CGContextFillPath(context);
}
but read the documentation first! hope it helps
Related
How would I animate a circle on the iPhone so that the arc starts at "0 degrees" and ends at "360 degrees"?
Advance Thanks,
Sat
You need to read the Quartz 2D Programming Guide's section on arcs. (I am assuming you are creating an app with the Cocoa Touch API, not a web app.) You also need to know how to set up a custom animation. You will have to create a custom UIView or CALayer to do the drawing, and create a property (arc degree) that can be animated with a CAAnimation object. Alternatively, you can control the animation using an NSTimer instead. You pretty much have to have a grasp of these classes (and others) to pull this off.
Here you can find a great sample code about circle animation:
http://www.iphonedevsdk.com/forum/iphone-sdk-development/24152-draw-animated-circle-iphone-using-core-graphics.html
you should read the documentation that Felixyz provided and if you want an example of how to animate the circle have a look over the MBProgressHUD at this link link text. The loader has two modes one with a UIViewActivityIndicator and a progress indicator (a filling circle that it is animated from 0 to 360 degress) i think the last mode is what you want.
the fallowing code is from copy/paste from that implementation that animates the circle:
- (void)drawRect:(CGRect)rect {
CGRect allRect = self.bounds;
CGRect circleRect = CGRectMake(allRect.origin.x + 2, allRect.origin.y + 2,
allRect.size.width - 4, allRect.size.height - 4);
CGContextRef context = UIGraphicsGetCurrentContext();
// Draw background
CGContextSetRGBStrokeColor(context, 1.0, 1.0, 1.0, 1.0); // white
CGContextSetRGBFillColor(context, 1.0, 1.0, 1.0, 0.1); // translucent white
CGContextSetLineWidth(context, 2.0);
CGContextFillEllipseInRect(context, circleRect);
CGContextStrokeEllipseInRect(context, circleRect);
// Draw progress
float x = (allRect.size.width / 2);
float y = (allRect.size.height / 2);
CGContextSetRGBFillColor(context, 1.0, 1.0, 1.0, 1.0); // white
CGContextMoveToPoint(context, x, y);
CGContextAddArc(context, x, y, (allRect.size.width - 4) / 2, -(PI / 2),
(self.progress * 2 * PI) - PI / 2, 0);
CGContextClosePath(context); CGContextFillPath(context);
}
but read the documentation first! hope it helps
I like the small pie progress bar like it's in Xcode, when search for a string in the project (Shift-Command-F), see picture.
How can this be called on iOS? I'd love to have it for a download queue.
Thanks in advance.
There's no stock circular deterministic progress view. Here's an example drawRect: you might use to implement such a thing.
- (void)drawRect:(CGRect)rect
{
CGFloat circleRadius = (self.bounds.size.width / 2) - (self.strokeWidth * 2);
CGPoint circleCenter = CGPointMake(CGRectGetMidX(rect), CGRectGetMidY(rect));
CGRect circleRect = CGRectMake(circleCenter.x - circleRadius, circleCenter.y - circleRadius, 2 * circleRadius, 2 * circleRadius);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);
// Draw stroked circle to delineate circle shape.
CGContextSetStrokeColorWithColor(context, self.fillColor.CGColor);
CGContextSetLineWidth(context, self.strokeWidth);
CGContextAddEllipseInRect(context, circleRect);
CGContextStrokePath(context);
// Draw filled wedge (clockwise from 12 o'clock) to indicate progress
self.progress = MIN(MAX(0.0, self.progress), 1.0);
CGFloat startAngle = -M_PI_2;
CGFloat endAngle = startAngle + (progress * 2 * M_PI);
CGContextSetFillColorWithColor(context, self.fillColor.CGColor);
CGContextMoveToPoint(context, circleCenter.x, circleCenter.x);
CGContextAddLineToPoint(context, CGRectGetMidX(circleRect), CGRectGetMinY(circleRect));
CGContextAddArc(context, circleCenter.x, circleCenter.y, circleRadius, startAngle, endAngle, NO);
CGContextClosePath(context);
CGContextFillPath(context);
CGContextRestoreGState(context);
}
You would need to create strokeWidth and fillColor properties to use this code directly, but it should be a start. Here's a sample project with a couple of examples.
Maybe you can use the code from MBProgressHUD there is a pie like this in some kind bigger.
I don't believe they're released a UIProgressView like this on iOS, but you can create it yourself. To put things in the right-hand side of a text view, use the rightView property (don't forget to set rightViewMode as well).
You can create a custom UIView for this (I doubt it'd be worth trying to subclass UIProgressView). I would probably hand-draw it with a drawRect rather than trying to use images or anything like that. Should be much easier to fill the correct percentage that way.
May be you can use SSPieProgressView
Try this: LSPieProgressView
It's an UIView Category, can show a pie progress overlay any UIView.
Code Example:
#import "UIView+LSPieProgress.h"
- (void)updateProgress
{
[self.button setProgress:self.progress];
}
First off, I am a quartz 2d noob. I have a pie chart, and basically want to make a line coming out of it diagonally, in the northeast quadrant, then go horizontal to the right, and have a label explaining what that section is. So, I have this code that draws the diagonal line. The line draws to the southeast. I expect that based on the iPhone origin being in the northwest quadrant. So I thought I could rotate my CTM first by 3pi/2, and then have that same code work. However, when I uncomment the CGContextRotateCTM() line, I get no line. I'm not sure what is happening here and where the logic error is. Thanks.
CGPoint circleCenter = CGPointMake(self.bounds.size.width / 2, self.bounds.size.height / 2);
CGContextSetFillColorWithColor(context, [[UIColor whiteColor] CGColor]);
CGContextSetRGBStrokeColor(context, 1.0, 1.0, 1.0, 1.0);
CGContextSetLineWidth(context, LINE_WIDTH);
CGContextBeginPath(context);
// CGContextRotateCTM(context, 3 * M_PI / 2);
CGContextMoveToPoint(context, circleCenter.x + self.CircleRadius / 2, circleCenter.y + self.CircleRadius / 2);
CGContextAddLineToPoint(context, circleCenter.x + self.CircleRadius / 2 + LINE_LENGTH, circleCenter.y + self.CircleRadius / 2 + LINE_LENGTH);
CGContextStrokePath(context);
It seems you are rotating your line out of sight. Always keep the center in mind that you rotate around - maybe also translate your CTM first.
Edit: To check, you might want to try very small rotations incrementally and watch where your line goes.
Ok I need to implement a simple measuring tool for an iPad app I am working on. I don't have any experience with drawing, so I am really struggling. When the user presses down on the view (in measuring mode) the line's origin starts. I need to then be able to draw a line to wherever the user drags their finger, as they are dragging their finger, and have it be straight the entire time too.
I have the logic that calculates the distance between two points working based on two UITapGestureRecognizers, but I am thinking I will need to implement the touchesBegan/Ended methods instead.
How can I draw a line as the user drags, and make it stay straight the entire time?
I just need a point in the right direction.
Thanks!!
Check out a good tutorial like this.
Skip to the section on drawing lines:
void draw1PxStroke(CGContextRef context, CGPoint startPoint, CGPoint endPoint,
CGColorRef color) {
CGContextSaveGState(context);
CGContextSetLineCap(context, kCGLineCapSquare);
CGContextSetStrokeColorWithColor(context, color);
CGContextSetLineWidth(context, 1.0);
CGContextMoveToPoint(context, startPoint.x + 0.5, startPoint.y + 0.5);
CGContextAddLineToPoint(context, endPoint.x + 0.5, endPoint.y + 0.5);
CGContextStrokePath(context);
CGContextRestoreGState(context);
}
and then here is the call to that method:
// Add in color section
CGColorRef separatorColor = [UIColor colorWithRed:208.0/255.0 green:208.0/255.0
blue:208.0/255.0 alpha:1.0].CGColor;
// Add at bottom
CGPoint startPoint = CGPointMake(0, 0);
CGPoint endPoint = CGPointMake(100, 20);
draw1PxStroke(context, startPoint, endPoint, separatorColor);
Is it possible to draw a filled circle with cocos2d ?
An outlined circle can be done using the drawCircle() function, but is there a way to fill it in a certain color? Perhaps by using pure OpenGL?
In DrawingPrimitives.m, change this in drawCricle:
glDrawArrays(GL_LINE_STRIP, 0, segs+additionalSegment);
to:
glDrawArrays(GL_TRIANGLE_FAN, 0, segs+additionalSegment);
You can read more about opengl primitives here:
http://www.informit.com/articles/article.aspx?p=461848
Here's a slight modification of ccDrawCircle() that lets you draw any slice of a circle. Stick this in CCDrawingPrimitives.m and also add the method header information to CCDrawingPrimitives.h:
Parameters: a: starting angle in radians, d: delta or change in angle in radians (use 2*M_PI for a complete circle)
Changes are commented
void ccDrawFilledCircle( CGPoint center, float r, float a, float d, NSUInteger totalSegs)
{
int additionalSegment = 2;
const float coef = 2.0f * (float)M_PI/totalSegs;
NSUInteger segs = d / coef;
segs++; //Rather draw over than not draw enough
if (d == 0) return;
GLfloat *vertices = calloc( sizeof(GLfloat)*2*(segs+2), 1);
if( ! vertices )
return;
for(NSUInteger i=0;i<=segs;i++)
{
float rads = i*coef;
GLfloat j = r * cosf(rads + a) + center.x;
GLfloat k = r * sinf(rads + a) + center.y;
//Leave first 2 spots for origin
vertices[2+ i*2] = j * CC_CONTENT_SCALE_FACTOR();
vertices[2+ i*2+1] =k * CC_CONTENT_SCALE_FACTOR();
}
//Put origin vertices into first 2 spots
vertices[0] = center.x * CC_CONTENT_SCALE_FACTOR();
vertices[1] = center.y * CC_CONTENT_SCALE_FACTOR();
// Default GL states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY
// Needed states: GL_VERTEX_ARRAY,
// Unneeded states: GL_TEXTURE_2D, GL_TEXTURE_COORD_ARRAY, GL_COLOR_ARRAY
glDisable(GL_TEXTURE_2D);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisableClientState(GL_COLOR_ARRAY);
glVertexPointer(2, GL_FLOAT, 0, vertices);
//Change to fan
glDrawArrays(GL_TRIANGLE_FAN, 0, segs+additionalSegment);
// restore default state
glEnableClientState(GL_COLOR_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glEnable(GL_TEXTURE_2D);
free( vertices );
}
Look into:
CGContextAddArc
CGContextFillPath
These will allow you to fill a circle without needing OpenGL
I also wonder this, but haven't really accomplished doing it. I tried using CGContext stuff that Grouchal tipped above, but I can't get it to draw anything on the screen. This is what I've tried:
-(void) draw
{
[self makestuff:UIGraphicsGetCurrentContext()];
}
-(void)makestuff:(CGContextRef)context
{
// Drawing lines with a white stroke color
CGContextSetRGBStrokeColor(context, 1.0, 1.0, 1.0, 1.0);
// Draw them with a 2.0 stroke width so they are a bit more visible.
CGContextSetLineWidth(context, 2.0);
// Draw a single line from left to right
CGContextMoveToPoint(context, 10.0, 30.0);
CGContextAddLineToPoint(context, 310.0, 30.0);
CGContextStrokePath(context);
// Draw a connected sequence of line segments
CGPoint addLines[] =
{
CGPointMake(10.0, 90.0),
CGPointMake(70.0, 60.0),
CGPointMake(130.0, 90.0),
CGPointMake(190.0, 60.0),
CGPointMake(250.0, 90.0),
CGPointMake(310.0, 60.0),
};
// Bulk call to add lines to the current path.
// Equivalent to MoveToPoint(points[0]); for(i=1; i<count; ++i) AddLineToPoint(points[i]);
CGContextAddLines(context, addLines, sizeof(addLines)/sizeof(addLines[0]));
CGContextStrokePath(context);
// Draw a series of line segments. Each pair of points is a segment
CGPoint strokeSegments[] =
{
CGPointMake(10.0, 150.0),
CGPointMake(70.0, 120.0),
CGPointMake(130.0, 150.0),
CGPointMake(190.0, 120.0),
CGPointMake(250.0, 150.0),
CGPointMake(310.0, 120.0),
};
// Bulk call to stroke a sequence of line segments.
// Equivalent to for(i=0; i<count; i+=2) { MoveToPoint(point[i]); AddLineToPoint(point[i+1]); StrokePath(); }
CGContextStrokeLineSegments(context, strokeSegments, sizeof(strokeSegments)/sizeof(strokeSegments[0]));
}
These methods are defined in a cocos node class, and the makestuff method I borrowed from a code example...
NOTE:
I'm trying to draw any shape or path and fill it. I know that the code above only draws lines, but I didn't wanna continue until I got it working.
EDIT:
This is probably a crappy solution, but I think this would at least work.
Each CocosNode has a texture (Texture2D *). Texture2D class can be initialized from an UIImage. UIImage can be initialized from a CGImageRef. It is possible to create a CGImageRef context for the quartz lib.
So, what you would do is:
Create the CGImageRef context for quartz
Draw into this image with quartz
Initialize an UIImage with this CGImageRef
Make a Texture2D that is initialized with that image
Set the texture of a CocosNode to that Texture2D instance
Question is if this would be fast enough to do. I would prefer if you could sort of get a CGImageRef from the CocosNode directly and draw into it instead of going through all these steps, but I haven't found a way to do that yet (and I'm kind of a noob at this so it's hard to actually get somewhere at all).
There is a new function in cocos2d CCDrawingPrimitives called ccDrawSolidCircle(CGPoint center, float r, NSUInteger segs). For those looking at this now, use this method instead, then you don't have to mess with the cocos2d code, just import CCDrawingPrimitives.h
I used this way below.
glLineWidth(2);
for(int i=0;i<50;i++){
ccDrawCircle( ccp(s.width/2, s.height/2), i,0, 50, NO);
}
I made multiple circle with for loop and looks like a filled circle.