I'd like to draw the lines of a simple ruler with Quartz2D, just for practice.
Since I have no idea about doing vector graphics programmatically on the iPhone, maybe someone can point me to a good tutorial to get started?
As Plamen points out, the Quartz 2D documentation is worth reading. Additionally, the course notes are available online (VoodooPad format) for my iPhone development course, where I devote an entire class to Quartz 2D drawing. The QuartzExamples sample application I created shows some more advanced drawing concepts, but Apple's QuartzDemo sample is a better place to start to see how you can do simple drawing.
As an example of drawing ticks for a ruler, the following is code that I have used to do something similar:
NSInteger minorTickCounter = majorTickInterval;
NSInteger totalNumberOfTicks = totalTravelRangeInMicrons / minorTickSpacingInMicrons;
CGFloat minorTickSpacingInPixels = currentHeight / (CGFloat)totalNumberOfTicks;
CGContextSetStrokeColorWithColor(context, [MyView blackColor]);
for (NSInteger currentTickNumber = 0; currentTickNumber < totalNumberOfTicks; currentTickNumber++)
{
CGContextMoveToPoint(context, leftEdgeForTicks + 0.5, round(currentTickNumber * minorTickSpacingInPixels) + 0.5);
minorTickCounter++;
if (minorTickCounter >= majorTickInterval)
{
CGContextAddLineToPoint(context, round(leftEdgeForTicks + majorTickLength) + 0.5, round(currentTickNumber * minorTickSpacingInPixels) + 0.5);
minorTickCounter = 0;
}
else
{
CGContextAddLineToPoint(context, round(leftEdgeForTicks + minorTickLength) + 0.5, round(currentTickNumber * minorTickSpacingInPixels) + 0.5);
}
}
CGContextStrokePath(context);
where currentHeight is the height of the area to cover, and [MyView blackColor] simply returns a CGColorRef representing the color black.
Related
I'm want to remove the blue part of the following images. How do I clip the inverse of the drawing? (I believe that's the correct way to ask the question)
Example code for Triangle: (If there's better triangle code, I'd accept that too!;)
int lineWidth = 4;
int w = size.size.width;
int h = size.size.height - lineWidth;
CGPoint top = CGPointMake(0+(w/2)+.5, 0);
CGContextClipToRect(ctx, CGRectMake(0, 0, w, h));
CGContextStrokePath(ctx);
CGContextMoveToPoint(ctx, top.x, top.y);
CGContextAddLineToPoint(ctx, top.x + (w/2), top.y + h );
CGContextAddLineToPoint(ctx, top.x - (w/2), top.y + h );
CGContextAddLineToPoint(ctx, top.x, top.y);
CGContextFillPath(ctx);
CGContextSetLineWidth(ctx, lineWidth);
CGContextSetLineCap(ctx, kCGLineCapRound);
CGContextMoveToPoint(ctx, top.x, top.y);
CGContextAddLineToPoint(ctx, top.x + (w/2), top.y + h );
CGContextAddLineToPoint(ctx, top.x - (w/2), top.y + h );
CGContextAddLineToPoint(ctx, top.x, top.y);
CGContextStrokePath(ctx);
An easy solution is to use UIBazierPath. Draw a Bazier Path according to your intentions. Then call [path addClip] method. It'll clip all the outside the closed path.
For example following code makes your view round cornered.
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:self.bounds
byRoundingCorners:UIRectCornerAllCorners
cornerRadii:CGSizeMake(6.0, 6.0)];
[path addClip];
To clip the inverse of any path just add an enclosing rect to your clip.
In this case, if I get you right, you want to clip around the red stroke of the shapes. Generally, that's not an easy task in quartz due to the line width of the stroked path. Of course, for these two shapes, we can compute the outline of the red stroke and clip around that. But a general solution would need a way to calculate the encompassing path, including the line width.
There are other ways that include bitmap masks—but the best solution would depend on what exactly are you drawing. I just had a similar problem which I could very elegantly and efficiently solve using a specialized quartz blend mode. This depends very much on the contents of the centext before drawing and how the result should be blended with the rest of the UI.
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.
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