I am creating a drawing app. One of the features is multicolour line drawing. It should work like user touch up the screen and leads on it drawing a line. Colour of the line changes smoothly. Like that http://www.examples.pavelgatilov.com/Screen%20Shot%202013-09-22%20at%208.37.42%20PM.png
I tried several approaches, but haven't been lucky.
My line drawing method is below:
- (void) drawLineFrom:(CGPoint)from to:(CGPoint)to width:(CGFloat)width
{
self.drawColor = toolColor;
UIGraphicsBeginImageContext(self.frame.size);
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextScaleCTM(ctx, 1.0f, -1.0f);
CGContextTranslateCTM(ctx, 0.0f, -self.frame.size.height);
if (drawImage != nil) {
CGRect rect = CGRectMake(0.0f, 0.0f, self.frame.size.width, self.frame.size.height);
CGContextDrawImage(ctx, rect, drawImage.CGImage);
}
CGContextSetLineCap(ctx, kCGLineCapRound);
CGContextSetLineWidth(ctx, width);
CGContextSetStrokeColorWithColor(ctx, self.drawColor.CGColor);
CGContextMoveToPoint(ctx, from.x, from.y);
CGContextAddLineToPoint(ctx, to.x, to.y);
CGContextStrokePath(ctx);
CGContextFlush(ctx);
drawImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
drawLayer.contents = (id)drawImage.CGImage;
}
Thanks for your help
Depending on exactly what colours, how they change and what effect you want / what happens with turns in the line, you may want to look at some combination of:
CGContextDrawLinearGradient with masking to the users drawn path.
colorWithPatternImage:
CGContextDrawLinearGradient drawn behind another layer and drawing transparency into the top layer with kCGBlendModeClear
Related
I am drawing a custom cell, to achieve a certain look I want. However, I want to perform a different drawing based on if the cell is select. I really do not just want the default colors.
I changed content's views background color the view in this method:
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
However, it just did not appear correctly, mainly it did not take into account the accessary and just colored it until the accessory indicator. Is there a better way to accomplish this?
- (void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
// Background
CGContextSetFillColorWithColor(context, CELL_BACKGROUND_COLOR);
CGContextMoveToPoint(context, 0.0f, 0.0f);
CGContextAddLineToPoint(context, rect.size.width, 0.0f);
CGContextAddLineToPoint(context, rect.size.width, rect.size.height);
CGContextAddLineToPoint(context, 0.0f, rect.size.height);
CGContextClosePath(context);
CGContextFillPath(context);
// Top line
CGContextSetStrokeColorWithColor(context, CELL_TOP_LINE_COLOR);
CGContextSetLineWidth(context, CELL_LINE_WIDTH);
CGContextSetLineCap(context, kCGLineCapSquare);
CGContextMoveToPoint(context, 0.0f, 0.0f);
CGContextAddLineToPoint(context, rect.size.width, 0.0f);
CGContextStrokePath(context);
//Bottom line
CGContextSetStrokeColorWithColor(context, CELL_BOTTOM_LINE_COLOR);
CGContextSetLineWidth(context, CELL_LINE_WIDTH);
CGContextSetLineCap(context, kCGLineCapSquare);
CGContextMoveToPoint(context, 0.0f, rect.size.height);
CGContextAddLineToPoint(context, rect.size.width, rect.size.height);
CGContextStrokePath(context);
}
I think you should modify your drawRect method and change how your color are set depending of the isSelected property. Changing the content's view background when the setSelected method will probably don't change anything since it will be overriding by the drawRect method.
I'm using this :
if(self.highlighted || self.selected) {
//set text color
textColor = [UIColor colorWithRed:204.0/255.0 green:255.0/255.0 blue:0 alpha:1.0];
//set background color
[[UIColor colorWithRed:0.0 green:0.0 blue:0.0 alpha:0.7] set];
CGContextFillRect(context, r);
}
Also check selectionStyleproperty of your cell.
I found a solution that works great, it also draws underneath the default accessory views, like the accessory indicator. I created custom view's called BackgroundView and SelectedBackgroundView, I just use the drawRect method to create a custom drawing. It works great, and performance seems to be okay. If anyone wants to see the full code let me know.
[cell setBackgroundView:[[BackgroundView alloc] init]];
[cell setSelectedBackgroundView:[[SelectedBackgroundView alloc] init]];
I have a CGContextRef and can draw on it and specify the alpha, and if I try and use it it works perfectly. However, I am trying to colourise it (currently either red or green), but whatever blend mode I choose, the alpha is set to 1 (because I am drawing with alpha as 1). Drawing it the correct colour is not really a viable option, as I would like to be able to colour UIImages loaded from the filesystem as well, so how should I achieve this?
Edit: Example code (width and height are predefined floats, points is an array of CGPoints all of which lie inside the context and color is a UIColor with an opacity of 100%) -
UIGraphicsBeginImageContext(CGSizeMake(width,height));
CGContextRef contextRef = UIGraphicsGetCurrentContext();
CGContextClearRect(contextRef, CGRectMake(0.0f, 0.0f, width, height));
CGContextSetRGBStrokeColor(contextRef, 0.0f, 0.0f, 0.0f, 1.0f);
CGContextSetRGBFillColor(contextRef, 1.0f, 1.0f, 1.0f, 1.0f);
CGContextSetLineWidth(contextRef, lineWidth);
CGContextBeginPath(contextRef);
CGContextMoveToPoint(contextRef, points[0].x, points[0].y);
for (int i = 1; i < 4; i++) {
CGContextAddLineToPoint(contextRef, points[i].x, points[i].y);
}
CGContextAddLineToPoint(contextRef, points[0].x, points[0].y); // Encloses shape
CGContextDrawPath(contextRef, kCGPathFillStroke);
[color setFill];
CGContextBeginPath(contextRef);
CGContextSetBlendMode(contextRef, kCGBlendModeMultiply);
CGContextAddRect(contextRef, CGRectMake(0.0f, 0.0f, width, height));
CGContextDrawPath(contextRef, kCGPathFill);
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
Thank you in advance for your help,
jrtc27
The problem was I needed a CGContextClipToMask. This meant my code required the following:
CGContextTranslateCTM(contextRef, 0, height);
CGContextScaleCTM(contextRef, 1.0, -1.0);
to convert the coordinates and then
CGRect rect = CGRectMake(0.0f, 0.0f, width, height);
CGContextClipToMask(contextRef, rect, UIGraphicsGetImageFromCurrentImageContext().CGImage);
to fix it.
I have a custom view with that code :
- (void)drawRect:(CGRect)rect
{
UIImage* theImage = [UIImage imageNamed:#"blue_arrow"];
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetStrokeColorWithColor(context, [UIColor whiteColor].CGColor);
CGContextSetFillColorWithColor(context, [UIColor whiteColor].CGColor);
CGContextSetLineWidth(context, 1.0);
CGContextSetTextDrawingMode(context, kCGTextFill);
CGPoint posOnScreen = self.center;
CGContextDrawImage(context, CGRectMake(posOnScreen.x - theImage.size.width/2,
posOnScreen.y - theImage.size.height/2,
theImage.size.width,
theImage.size.height),
theImage .CGImage);
}
The image is an arrow, pointiing to the top.
But when drawn, it is draw upside down, pointing to bottom.
What causes that problem ?
How may I correct that, naturally without side effect ?
This is because Core Graphics is using a flipped coordinate system.
You could try flipping the object you're drawing to using the isFlipped property.
Or you could try using this code in your drawing method (Source):
CGContextTranslateCTM(context, 0, image.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
I'm currently drawing a line using Core Graphics. It's really bare bones and simple.
- (void)drawRect:(CGRect)rect {
CGContextRef c = UIGraphicsGetCurrentContext();
CGFloat red[4] = {1.0f, 0.0f, 0.0f, 1.0f};
CGContextSetStrokeColor(c, red);
CGContextBeginPath(c);
CGContextMoveToPoint(c, 5.0f, 5.0f);
CGContextAddLineToPoint(c, 300.0f, 600.0f);
CGContextSetLineWidth(c, 25);
CGContextSetLineCap(c, kCGLineCapRound);
CGContextStrokePath(c);
}
This works well. Let's say that we wanted to draw a custom style line. Say we wanted to imitate the style of a crayon for example. And that the designer handed your crayon style images: http://imgur.com/a/N40ig
To do accomplish this effect I think I need to do something like this:
Create a special colored versions of crayonImage1-crayonImage4
Every time you add a line to line you use one of the crayonImages
You alternate the crayonImages every time you draw a point.
Step 1 makes sense. I can use the following method:
- (UIImage *)image:(UIImage *)img withColor:(UIColor *)color {
// begin a new image context, to draw our colored image onto
UIGraphicsBeginImageContext(img.size);
// get a reference to that context we created
CGContextRef context = UIGraphicsGetCurrentContext();
// set the fill color
[color setFill];
// translate/flip the graphics context (for transforming from CG* coords to UI* coords
CGContextTranslateCTM(context, 0, img.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
// set the blend mode to color burn, and the original image
CGContextSetBlendMode(context, kCGBlendModeColorBurn);
CGRect rect = CGRectMake(0, 0, img.size.width, img.size.height);
CGContextDrawImage(context, rect, img.CGImage);
// set a mask that matches the shape of the image, then draw (color burn) a colored rectangle
CGContextClipToMask(context, rect, img.CGImage);
CGContextAddRect(context, rect);
CGContextDrawPath(context,kCGPathFill);
// generate a new UIImage from the graphics context we drew onto
UIImage *coloredImg = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
//return the color-burned image
return coloredImg;
}
I'm unsure how I can complete steps 2 and 3. Is there an API in CoreGraphics for setting an image as the point of line? If so what is it and how can I use it?
Thanks in advance,
-David
Start with the following example: http://www.ifans.com/forums/showthread.php?t=132024
But for brushes, don't draw a line. Simply draw the brush image using CGContextDrawImage.
Basically, you simply draw an image for every touch.
Does anyone know how to animate a radar animation like the image below?
alt text http://img197.imageshack.us/img197/7124/circle0.png
With it growing outwards? I have to draw the circle using Quartz or some other way?
To draw a static circle in a UIView subclass:
- (void)drawRect:(CGRect)rect
{
CGRect rect = { 0.0f, 0.0f, 100.0f, 100.0f };
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetRGBStrokeColor(context, 1.0, 0.0, 0.0, 1.0);
CGContextSetLineWidth(context, 2.0f);
CGContextAddEllipseInRect(context, rect);
CGContextStrokePath(context);
}
From there you just need to animate it with a timer, vary the size of the rect, and add more circles.