So ... I have this app that draws shapes and then the user can color them. I made a tabbar that has 4 shapes and when one is clicked the respective shape is drawn. I took the drawing idea from the quartzDemo. So I have a shape general class and then shape_name subclasses of shape. Here is the code for the Square.
#implementation Square
- (void)drawInContext:(CGContextRef)context {
CGContextSetRGBStrokeColor(context, 0.0, 0.0, 0.0, 1.0);
CGContextSetLineWidth(context, 1.0);
CGContextBeginPath(context);
CGContextMoveToPoint(context, 1, 1);
CGContextAddLineToPoint(context, 1, 79);
CGContextAddLineToPoint(context, 79, 79);
CGContextAddLineToPoint(context, 79, 1);
CGContextAddLineToPoint(context, 1, 1);
CGContextClosePath(context);
CGContextStrokePath(context);
}
#end
When a shape is tapped a color menu appears and I want to change the color when a button from the menu is tapped. How can I do this.
Ty in advance.
I suggest adding a property to your base shape class like this:
#property(nonatomic,retain) UIColor* fillColor;
In your menu implementation set the value of the property to a UIColor object of your user's choice and send the shape setNeedsDisplay. Then modify your drawInContext: method as follows:
CGContextSetFillColorWithColor(context, self.fillColor.CGColor);
// ... your current drawing code goes here
// replace the last line, with CGContextStrokePath(), with this:
CGContextDrawPath(context, kCGPathFillStroke);
Related
I have the following code:
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
// Left frame.
CGContextAddRect(context, CGRectMake(0, 0,
frameWidth,
self.frameHeight));
CGContextSetFillColorWithColor(context, [UIColor colorWithRed:frameBorderColorRed
green:frameBorderColorGreen
blue:frameBorderColorBlue
alpha:frameBorderColorAlpha].CGColor);
CGContextFillPath(context);
CGContextAddRect(context, CGRectMake(frameBorderWidth,
frameBorderWidth,
frameWidth - (2*frameBorderWidth),
self.frameHeight - (2*frameBorderWidth)));
CGContextSetBlendMode(context, kCGBlendModeClear);
CGContextFillPath(context);
// Right frame.
CGContextAddRect(context, CGRectMake(frameWidth + distanceBetweenFrames,
0,
frameWidth,
self.frameHeight));
CGContextSetFillColorWithColor(context, [UIColor colorWithRed:frameBorderColorRed
green:frameBorderColorGreen
blue:frameBorderColorBlue
alpha:frameBorderColorAlpha].CGColor);
CGContextFillPath(context);
CGContextAddRect(context, CGRectMake(frameWidth + distanceBetweenFrames + frameBorderWidth,
frameBorderWidth,
frameWidth - (2*frameBorderWidth),
self.frameHeight - (2*frameBorderWidth)));
CGContextSetBlendMode(context, kCGBlendModeClear);
CGContextFillPath(context);
}
The left frame is showing but the right frame isn't. However, if I were to comment out the left frame, the right frame shows. I have also tried drawing the lines themselves as in the docs:
https://developer.apple.com/library/mac/documentation/GraphicsImaging/Reference/CGContext/index.html#//apple_ref/c/func/CGContextAddRect
To no avail.
I would consider using CoreAnimation since it uses GPU.
Besides the problem is that you close current path by:
CGContextFillPath(context);
and then try to use:
CGContextAddRect
As documentation says: "Adds a rectangular path to the current path."
You need to use method below to add another path:
CGContextMoveToPoint
More detailed information you can read there:
https://developer.apple.com/library/mac/documentation/GraphicsImaging/Reference/CGContext/index.html
I'm using CoreGraphics to draw a line with circular endpoints and a drop shadow using the code below:
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetShadowWithColor(UIGraphicsGetCurrentContext(), CGSizeMake(0, 0), 4.0, [UIColor blackColor].CGColor);
CGContextSetFillColorWithColor(context, [UIColor whiteColor].CGColor);
CGContextFillEllipseInRect(context, CGRectMake(_startPoint.x - 5, _startPoint.y - 5, 10, 10));
CGContextFillEllipseInRect(context, CGRectMake(_endPoint.x - 5, _endPoint.y - 5, 10, 10));
CGContextSetStrokeColorWithColor(context, [UIColor whiteColor].CGColor);
CGContextSetLineWidth(context, 3.0);
CGContextMoveToPoint(context, _startPoint.x, _startPoint.y);
CGContextAddLineToPoint(context, _endPoint.x, _endPoint.y);
CGContextDrawPath(context, kCGPathStroke);
However, this results in something like this:
The shadow is being applied to the line and circles individually. Instead, what I want is for the shadow to be applied to the 'compound' shape created by all three (i.e. only use the compound alpha channel as the basis for the shadow), like so:
Any idea how I might achieve this effect instead? (note that I cannot use the layer.shadow property of my UIView as this line is rendered in response to touch drag events and that method is not performant enough).
This is exactly what Transparency Layers are for.
I think you'll find that the examples look familiar.
So, I have this code, that draws a circle with a line around it.
- (void)drawRect:(CGRect)rect
{
CGContextRef contextRef = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(contextRef, 40.0);
CGContextSetRGBFillColor(contextRef, 0.0, 0.0, 0.0, 1.0);
CGContextSetRGBStrokeColor(contextRef, 132.0/255.0, 128.0/255.0, 128.0/255.0, 1.0);
CGContextFillEllipseInRect(contextRef, rect);
CGContextStrokeEllipseInRect(contextRef, rect);
}
The problem is that half of the width of the line falls outside the rect.
So, what I would like to do, is to create an inset relative to the line width, to compensate for this.
Or maybe there is a method, that tells my line to remain inside the boundaries of the circle?
Before filling,
rect = CGRectInset(rect, 1, 1);
I am using Quartz 2D in a UIView object to draw several curves, like this example:
This is the code I have right now (I have omitted the calculation of control points and other stuff):
for (int i = 1; i < points.count; ++i) {
CGContextMoveToPoint(context, previousXValue, previousYValue);
CGContextAddCurveToPoint(context,
firstControlPointXValue, firstControlPointYValue,
secondControlPointXValue, secondControlPointYValue,
xValue, yValue
);
// CGContextFillPath(context);
CGContextStrokePath(context);
}
Now I would like to fill the area below the curve, but if I use CGContextFillPath the result is the following:
which makes sense, because according to Quartz 2D documentation:
When you fill the current path, Quartz acts as if each subpath contained in the path were closed. It then uses these closed subpaths
and calculates the pixels to fill.
Then I tried to move the path to the lower right corner and close the path, but the fill method doesn't have any effect:
CGContextMoveToPoint(context, rect.size.width, rect.size.height);
CGContextClosePath(context);
CGContextFillPath(context);
How could I fill the whole area below the curve and not just the subarea closed in every subpath?
EDIT:
I have found a temporary solution: drawing a shape using the curve and two vertical lines on each subpath:
for (int i = 1; i < points.count; ++i) {
// Draw the curve
CGContextMoveToPoint(context, previousXValue, previousYValue);
CGContextAddCurveToPoint(context,
firstControlPointXValue, firstControlPointYValue,
secondControlPointXValue, secondControlPointYValue,
xValue, yValue
);
CGContextStrokePath(context);
// Draw a shape using curve's initial and final points
CGContextMoveToPoint(context, previousXValue, rect.size.height);
CGContextAddLineToPoint(context, previousXValue, previousYValue);
CGContextAddCurveToPoint(context,
firstControlPointXValue, firstControlPointYValue,
secondControlPointXValue, secondControlPointYValue,
xValue, yValue
);
CGContextAddLineToPoint(context, xValue, rect.size.height);
CGContextFillPath(context);
}
I don't know if this is overkill, so improvements are also welcomed. Besides, I'm getting this result:
Note that vertical lines appear as a result of drawing the subareas next to each other. How can that be avoided?
The idea is right, but you should not fill the individual curves, but rather, create a single path and then fill that. So, start with CGContextMoveToPoint to the lower left corner, CGContextAddLineToPoint to your first point, do the CGContextAddCurveToPoint for all of the curves, and at the end, do a CGContextAddLineToPoint to the lower right corner (and you might as well do a CGContextClosePath for good measure).
This is a simplified example, where I just have an array of points, but it illustrates the idea:
- (void)drawRect:(CGRect)rect
{
if (!self.points)
[self configurePoints];
DataPoint *point;
CGContextRef context = UIGraphicsGetCurrentContext();
CGColorRef color = [[UIColor redColor] CGColor];
CGContextSetStrokeColorWithColor(context, color);
CGContextSetFillColorWithColor(context, color);
point = self.points[0];
CGContextMoveToPoint(context, point.x, self.bounds.size.height);
CGContextAddLineToPoint(context, point.x, point.y);
for (point in self.points)
{
// you'd do your CGContextAddCurveToPoint here; I'm just adding lines
CGContextAddLineToPoint(context, point.x, point.y);
}
point = [self.points lastObject];
CGContextAddLineToPoint(context, point.x, self.bounds.size.height);
CGContextClosePath(context);
CGContextDrawPath(context, kCGPathFillStroke);
}
I have these sizes of circles that changes using a UISlider and draws on touch.
Now, i am trying give it a new style on how it will draw on the screen on touch, and it's like this, when the user holds the touch (for example he's on the middle size circle) it'll start form the smallest size and stops on its current size but when the user just tap the screen very quick it'll just draw the smallest size no matter what's the size is...
how will i able to that??
here's my current code:
On touchesBegan:
if(!touchSwiped) {
UIGraphicsBeginImageContext(self.view.frame.size);
[drawView.image drawInRect:CGRectMake(0, 0, 768, 1024)];
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetLineCap(context, kCGLineCapRound);
CGContextSetLineWidth(context, lineWidth);
CGContextSetRGBStrokeColor(context, color1, color2, color3, alpha);
CGContextSetBlendMode(context, blendMode);
CGContextMoveToPoint(context, endingPoint.x, endingPoint.y);
CGContextAddLineToPoint(context, endingPoint.x, endingPoint.y);
CGContextStrokePath(context);
CGContextFlush(context);
drawView.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}
Thanks!
UILongPressGestureRecognizer may be what you're looking for.