Pushing/Popping Graphics Context - ios5

I've been going through the Standford University IOS lessons and I am stuck on understanding what UIGraphicsPushContext and UIGraphicsPopContext does (lesson 4). From the notes, it is explained that these methods can be used to avoid affecting the current graphics states from other utility methods. Here is the example provided in the notes:
- (void)drawGreenCircle:(CGContextRef)ctxt {
UIGraphicsPushContext(ctxt);
[[UIColor greenColor] setFill];
// draw my circle
UIGraphicsPopContext();
}
- (void)drawRect:(CGRect)aRect {
CGContextRef context = UIGraphicsGetCurrentContext();
[[UIColor redColor] setFill];
// do some stuff
[self drawGreenCircle:context];
// do more stuff and expect fill color to be red
}
However, when I try to test this, I don't seem to get this result. Below is a simple test case I've made that gets the current context in drawRect, sets the color, calls a utility method that sets the color to green, returns to drawRect and draws a line. I would expect based on the notes on the Standford site that the line would be red since I pushed/popped the context in my drawGreenCircle. (I realize I am not actually drawing any circle in drawGreenCircle) But I get a green line in drawRect. It seems that my drawGreenCircle method did change the color and did not revert it back to red. I would like to know what I am missing about this concept.
- (void)drawGreenCircle:(CGContextRef)ctxt {
//Doesn't actually draw circle --Just testing if the color reverts
//to red in drawRect after this returns
UIGraphicsPushContext(ctxt);
[[UIColor greenColor] setStroke];
UIGraphicsPopContext();
}
- (void)drawRect:(CGRect)aRect {
CGContextRef context = UIGraphicsGetCurrentContext();
[[UIColor redColor] setStroke];
// do some stuff
[self drawGreenCircle:context];
CGContextBeginPath(context);
CGContextMoveToPoint(context, 100, 100);
CGContextAddLineToPoint(context, 500, 100);
CGContextClosePath(context);
CGContextDrawPath(context, kCGPathFillStroke);
}

You need CGContextSaveGState and CGContextRestoreGState instead. The following code works:
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
[[UIColor greenColor] setFill];
[[UIColor blackColor] setStroke];
[self drawBlueCircle:context];
CGContextMoveToPoint(context, 55, 5);
CGContextAddLineToPoint(context, 105, 100);
CGContextAddLineToPoint(context, 5, 100);
CGContextClosePath(context);
CGContextDrawPath(context, kCGPathFillStroke);
}
- (void)drawBlueCircle:(CGContextRef)context {
CGContextSaveGState(context);
[[UIColor blueColor] setFill];
CGContextAddEllipseInRect(context, CGRectMake(0, 0, 100, 100));
CGContextDrawPath(context, kCGPathFillStroke);
CGContextRestoreGState(context);
}
Source

Related

How can i call this method in self.view?

I am making this method for drawing line now what I want is to call it in another methods how can I do that, please help me out of this that how can I add this in self.view?
- (void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(context, 5.0);
CGContextSetStrokeColorWithColor(context, [UIColor blueColor].CGColor);
CGFloat dashArray[] = {2,6,4,2};
CGContextSetLineDash(context, 3, dashArray, 4);
CGContextMoveToPoint(context, 10, 200);
CGContextAddQuadCurveToPoint(context, 150, 10, 300, 200);
CGContextStrokePath(context);
}
Mark the view as needing a redraw:
[self.view setNeedsDisplay];
See reference.
[self.view setNeedsDisplay];
This will call your draw rect method.

how can I fill in drawRect with color

I have a drawRect and I want to fill in the rect specified with a certain color. How do I do so? So far I have tried the following:
- (void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, [UIColor colorWithWhite:29/255.f alpha:1.0].CGColor);
CGContextFillRect(context, rect);
}
This doesn't seem to work
[[UIColor colorWithWhite:29/255.f alpha:1.0] setFill];
UIRectFill(rect);
This is the simplest way

CoreGraphics taking a while to show on a large view - can i get it to repeat pixels?

This is my coregraphics code:
void drawTopPaperBackground(CGContextRef context, CGRect rect) {
CGRect paper3 = CGRectMake(10, 14, 300, rect.size.height - 14);
CGRect paper2 = CGRectMake(13, 12, 294, rect.size.height - 12);
CGRect paper1 = CGRectMake(16, 10, 288, rect.size.height - 10);
//Shadow
CGContextSetShadowWithColor(context, CGSizeMake(0,0), 10, [[UIColor colorWithWhite:0 alpha:0.5]CGColor]);
CGPathRef path = createRoundedRectForRect(paper3, 0);
CGContextSetFillColorWithColor(context, [[UIColor blackColor] CGColor]);
CGContextAddPath(context, path);
CGContextFillPath(context);
//Layers of paper
CGContextSaveGState(context);
drawPaper(context, paper3);
drawPaper(context, paper2);
drawPaper(context, paper1);
CGContextRestoreGState(context);
}
void drawPaper(CGContextRef context, CGRect rect) {
//Shadow
CGContextSaveGState(context);
CGContextSetShadowWithColor(context, CGSizeMake(0,0), 1, [[UIColor colorWithWhite:0 alpha:0.5]CGColor]);
CGPathRef path = createRoundedRectForRect(rect, 0);
CGContextSetFillColorWithColor(context, [[UIColor blackColor] CGColor]);
CGContextAddPath(context, path);
CGContextFillPath(context);
CGContextRestoreGState(context);
//Gradient
CGContextSaveGState(context);
CGColorRef startColor = [UIColor colorWithWhite:0.92 alpha:1.0].CGColor;
CGColorRef endColor = [UIColor colorWithWhite:0.94 alpha:1.0].CGColor;
CGRect firstHalf = CGRectMake(rect.origin.x,
rect.origin.y, rect.size.width / 2, rect.size.height);
CGRect secondHalf = CGRectMake(rect.origin.x + (rect.size.width / 2),
rect.origin.y, rect.size.width / 2, rect.size.height);
drawVerticalGradient(context, firstHalf, startColor, endColor);
drawVerticalGradient(context, secondHalf, endColor, startColor);
CGContextRestoreGState(context);
CGContextSaveGState(context);
CGRect redRect = rectForRectWithInset(rect, -1);
CGMutablePathRef redPath = createRoundedRectForRect(redRect, 0);
CGContextSaveGState(context);
CGContextSetStrokeColorWithColor(context, [[UIColor blackColor] CGColor]);
CGContextAddPath(context, path);
CGContextClip(context);
CGContextAddPath(context, redPath);
CGContextSetShadowWithColor(context, CGSizeMake(0, 0), 15.0, [[UIColor colorWithWhite:0 alpha:0.1] CGColor]);
CGContextStrokePath(context);
CGContextRestoreGState(context);
}
The view is a UIScrollView, which contains a textview. Every time the user types something and goes onto a new line, I call [self setNeedsDisplay]; and it redraws the code. But when the view starts to get long - around 1000 height, it has very noticeable lag. How can i make this code more efficient? Can i take a line of pixels and make it just repeat that, or stretch it, all the way down?
You can draw your repeating background once, into an offscreen context, pull out the image from it, then create a UIColor with the pattern image and set that as your background colour. The image will then be tiled for you.
You start an image context with UIGraphicsBeginImageContextWithOptions(). The options include the size of your expected image - I assume this is the width of your scrollview and x pixels high, the opacity, and scale. Send 0 for the scale for automatic retina support.
You can then act as if you were within a drawRect method - so your functions above can be called as normal.
Then, extract the image:
UIImage *background = UIGraphicsGetImageFromCurrentImageContext();
End the image context:
UIGraphicsEndImageContext();
Then create the colour:
UIColor *backgroundColor = [UIColor colorWithPatternImage:background];
And set that as the background for your scrollview. Typed on a phone, so apologies for ... everything.

Using UIGraphicsPushContext(context) and UIGraphicsPopContext()

Very frustrating experience understanding UIGraphicsPushContext(context) and UIGraphicsPopContext()
My understanding is that I can set attributes of the context like the stroke color and then push the context on a stack so I can set a new color for the current context. When I'm done doing that I can return to the context by popping it.
Below is my code. When I run the code below two lines are drawn in blue. What I expect to happen is: that I first set the color green. Go to the blueLine function and push the green context. Draw in blue. Then pop the green context. Allowing the drawLine function to drawn in green.
Here is a screenshot of what is drawn (two blue lines): http://dl.dropbox.com/u/1207310/iOS%20Simulator%20Screen%20shot%20Feb%205%2C%202012%209.00.35%20PM.png
Any help is GREATLY appreciated! Thank you.
- (void)drawBlueLine:(CGContextRef)context
{
UIGraphicsPushContext(context);
[[UIColor blueColor] setStroke];
CGContextBeginPath(context);
CGContextMoveToPoint(context, self.bounds.origin.x, 100);
CGContextAddLineToPoint(context, self.bounds.origin.x+self.bounds.size.width, 200);
CGContextStrokePath(context);
UIGraphicsPopContext();
}
- (void)drawLine:(CGContextRef)context
{
UIGraphicsPushContext(context);
CGContextBeginPath(context);
CGContextMoveToPoint(context, self.bounds.origin.x, 200);
CGContextAddLineToPoint(context, self.bounds.origin.x+self.bounds.size.width, 300);
CGContextStrokePath(context);
UIGraphicsPopContext();
}
- (void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
[[UIColor redColor] setStroke];
CGContextSetLineWidth(context, 5.0);
[self drawBlueLine:context];
[self drawLine:context];
}
Or shouldn't this work?
- (void)drawLine:(CGContextRef)oldContext
{
UIGraphicsPushContext(oldContext); //makes oldContext the current context but there is a copy on the stack
[[UIColor blueColor] setStroke];
CGContextBeginPath(oldContext);
CGContextMoveToPoint(oldContext, self.bounds.origin.x, 200);
CGContextAddLineToPoint(oldContext, self.bounds.origin.x+self.bounds.size.width, 300);
CGContextStrokePath(oldContext);
UIGraphicsPopContext(); //previous oldContext is moved back into place containing red?
}
- (void)drawRect:(CGRect)rect
{
CGContextRef oldContext = UIGraphicsGetCurrentContext();
[[UIColor redColor] setStroke];
[self drawLine:oldContext];
//anything I draw here should be red. Shouldn't it?`
What UIGraphicsPushContext and UIGraphicsPopContext does as change which context is being used for drawing, not necessarily save and restore any given context's state. What your code is doing is simply repetitively replacing the context that was current prior to the call to drawRect: with the context you grab with UIGraphicsGetCurrentContext (they are likely to be the same anyway).
If you want to save an restore a given context's state, use CGContextSaveGState and CGContextRestoreGState instead.
The problem is the way you are setting the color, you need to use
CGContextSetStrokeColorWithColor(context, color)
like this:
CGContextSetStrokeColorWithColor(context, [UIColor blueColor].CGColor)
and
CGContextSetStrokeColorWithColor(context, [UIColor redColor].CGColor)
so that the color is connected to the context.
[[UIColor redColor] setStroke];
is changing the color for all strokes...I'm curious if it would overide the above function if it was set for the context, I think it would not...

iPhone CGContext: drawing two lines with two different colors

I am having some troubles using the CGContext with an iPhone app. I am trying to draw several lines with different colors, but all the lines always end up having the color which was used last. I tried several approaches, but haven't been lucky.
I set up a small sample project to deal with that issue. This is my code, I use in the drawRect method. I am trying to draw a red and a blue line:
- (void)drawRect:(CGRect)rect{
NSLog(#"drawrect!");
CGContextRef bluecontext = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(bluecontext, 2.0);
CGContextSetStrokeColorWithColor(bluecontext, [UIColor blueColor].CGColor);
CGContextMoveToPoint(bluecontext, 1, 1);
CGContextAddLineToPoint(bluecontext, 100, 100);
CGContextSetStrokeColorWithColor(bluecontext, [UIColor redColor].CGColor);
CGContextAddLineToPoint(bluecontext, 200, 100);
CGContextStrokePath(bluecontext);
}
thanks for your help
Insert this code just before you set the stroke color the second time:
CGContextStrokePath(bluecontext);
CGContextBeginPath(bluecontext);
All the AddLine and AddOther calls are building a path. The path is drawn with a call like StrokePath, using the most recently set colors and other attributes. You are trying to draw two separate paths, so you must call Begin and Stroke for each path. Begin is sort of implicit when you start drawing, although it does not hurt to call it yourself. The basic flow of drawing is:
CGContextBeginPath(bluecontext); // clears any previous path
// add lines, curves, rectangles, etc...
CGContextStrokePath(bluecontext); // renders the path
Thats what you need.
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextClearRect(context, rect);
CGContextSetLineWidth(context, 2.0);
CGContextBeginPath(context);
CGContextSetStrokeColorWithColor(context, [UIColor orangeColor].CGColor);
CGContextMoveToPoint(context, 1, 1);
CGContextAddLineToPoint(context, 100, 100);
CGContextStrokePath(context); // and draw orange line}
CGContextBeginPath(context);
CGContextSetStrokeColorWithColor(context, [UIColor blueColor].CGColor);
CGContextMoveToPoint(context, 100, 100);
CGContextAddLineToPoint(context, 200, 100);
CGContextStrokePath(context); // draw blue line
I think this could be working.
CGContextRef bluecontext = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(bluecontext, 2.0);
CGContextSetStrokeColorWithColor(bluecontext, [UIColor blueColor].CGColor);
CGContextMoveToPoint(bluecontext, 1, 1);
CGContextAddLineToPoint(bluecontext, 100, 100);
CGContextStrokePath(bluecontext); // draw blue line
CGContextSetStrokeColorWithColor(bluecontext, [UIColor redColor].CGColor);
CGContextAddLineToPoint(bluecontext, 200, 100);
CGContextStrokePath(bluecontext); // and draw red line
If you are interested in the way it looks in a loop:
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(context, 2.0);
CGPoint startingPoint = [[pointsArray objectAtIndex:0] CGPointValue];
CGContextMoveToPoint(context, startingPoint.x, startingPoint.y); //start at this point
for (int i = 1; i < [pointsArray count]; i++) {
CGContextBeginPath(context);
//start at the previous point
CGContextMoveToPoint(context,
[[pointsArray objectAtIndex:i-1] CGPointValue].x,
[[pointsArray objectAtIndex:i-1] CGPointValue].y);
CGPoint point = [[pointsArray objectAtIndex:i] CGPointValue];
if (point.y < 50) { // if y is less then 50 use red color
CGContextSetStrokeColorWithColor(context, [UIColor redColor].CGColor);
} else { // else use blue color
CGContextSetStrokeColorWithColor(context, [UIColor blueColor].CGColor);
}
CGContextAddLineToPoint(context, point.x, point.y); //draw to this point
CGContextStrokePath(context);
}
}