iOS - change one pixel of a view - ios

In my app I need to change the color of one pixel(of a view) to black, I need to do this in the (void)touchesMoved:withEvent: of a custom gester recognizer, which will be applied on the view. (I am making a pen like thing).
My question is what is the simplest draw a line behind the gester recognizer, the line would stay after the gester recognizer is moved/
Let me know if you need any more information.

Drawing in a view is done in drawRect: http://developer.apple.com/library/ios/documentation/UIKit/Reference/UIView_Class/UIView/UIView.html#//apple_ref/doc/uid/TP40006816-CH3-BBCDGJHF
You'll need to set an instance variable or property in touchesMoved to the point that you need to paint then call [self setNeedsDisplay] and drawRect will get invoked. In drawRect you will draw a one pixel rectangle.
Something like this, modify to suit your needs:
- (void)drawRect:(CGRect)rect {
[super drawRect:rect];
CGRect rectangle = CGRectMake(self.cachedPoint.x, self.cachedPoint.y, 1, 1);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetRGBFillColor(context, 1.0, 0.0, 0.0, 1.0);
CGContextSetRGBStrokeColor(context, 1.0, 0.0, 0.0, 1.0);
CGContextFillRect(context, rectangle);
}

Related

UIView draw rect retain's the previous drawing and does not clear on view.transform

I have the following code to show marker in a UIView. The marker show's well, and once we try to pinch zoom and scale the UIView using the transform the first drawing remains as it is, even after calling setNeedsDisplay.
My Custom UIView subclass has the following code
- (void)drawRect:(CGRect)rect
{
// Drawing code
CGFloat w=20.0f;
CGFloat h=8.0f;
CGContextRef context=UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, [UIColor blueColor].CGColor);
CGContextClearRect(context,self.bounds);
CGContextSetStrokeColorWithColor(context, [UIColor redColor].CGColor);
CGContextSetLineCap(context, 2.0);
CGMutablePathRef leftMarker=CGPathCreateMutable();
CGPathMoveToPoint(leftMarker, NULL, 0, 0);
CGPathAddLineToPoint(leftMarker, NULL, w, 0);
CGPathAddLineToPoint(leftMarker,NULL, w, h);
CGPathAddLineToPoint(leftMarker,NULL, h, h);
CGPathAddLineToPoint(leftMarker,NULL,h, w);
CGPathAddLineToPoint(leftMarker,NULL,0, w);
CGPathAddLineToPoint(leftMarker,NULL, 0, 0);
CGContextAddPath(context, leftMarker);
CGContextDrawPath(context, kCGPathFill);
const CGAffineTransform rightMarkerTransform=CGAffineTransformMakeRotateTranslate(DEGREES_TO_RADIANS(90),self.frame.size.width,0);
CGPathRef rightMarker=CGPathCreateCopyByTransformingPath(path, &rightMarkerTransform);
CGContextAddPath(context, rightMarker);
CGContextDrawPath(context, kCGPathFill);
const CGAffineTransform leftMarkerBottomTransform=CGAffineTransformMakeRotateTranslate(DEGREES_TO_RADIANS(270),0,self.frame.size.height);
CGPathRef leftMarkerbottom=CGPathCreateCopyByTransformingPath(path, &leftMarkerBottomTransform);
CGContextAddPath(context, leftMarkerbottom);
CGContextDrawPath(context, kCGPathFill);
const CGAffineTransform rightMarkerBottomTransform=CGAffineTransformMakeRotateTranslate(DEGREES_TO_RADIANS(180),self.frame.size.width,self.frame.size.height);
CGPathRef rightMarkerBottom=CGPathCreateCopyByTransformingPath(path, &rightMarkerBottomTransform);
CGContextAddPath(context, rightMarkerBottom);
CGContextDrawPath(context, kCGPathFill);
CGPathRelease(rightMarker);
CGPathRelease(leftMarkerbottom);
CGPathRelease(rightMarkerBottom);
CGPathRelease(leftMarker);
}
The pinch zoom code is listed below
CGFloat lastScale;
-(void) handlepinchGesture:(UIPinchGestureRecognizer*)gesture{
UIView *gestureV=gesture.view;
CGFloat scale=gesture.scale;
switch (gesture.state) {
case UIGestureRecognizerStateBegan:
if(lastScale<1.0){
lastScale=1.0;
}
scale=lastScale;
break;
default:
break;
}
if(scale<1.0){
scale=1.0;
}
lastScale=scale;
gestureV.transform=CGAffineTransformMakeScale(scale, scale);
//Even this does not work ….[gestureV setNeedsDisplay];
gesture.scale=scale;
}
Make you sure have this set (but it should be defaulted to YES).
self.clearsContextBeforeDrawing = YES;
From Apple's Docs in UIView
When
set to YES, the drawing buffer is automatically cleared to transparent
black before the drawRect: method is called. This behavior ensures
that there are no visual artifacts left over when the view’s contents
are redrawn. If the view’s opaque property is also set to YES, the
backgroundColor property of the view must not be nil or drawing errors
may occur. The default value of this property is YES.
If you set the value of this property to NO, you are responsible for
ensuring the contents of the view are drawn properly in your drawRect:
method. If your drawing code is already heavily optimized, setting
this property is NO can improve performance, especially during
scrolling when only a portion of the view might need to be redrawn.
You need to set the background color to something other than nil.
From DBD's answer (which is taken from the docs):
If the view’s opaque property is also set to YES, the backgroundColor property of the view must not be nil or drawing errors may occur.
You also need to make sure self.clearsContextBeforeDrawing is set to YES.

UIImage masking with gesture

I'm trying to achieve selective color feature in iOS. I personally think that first draw shape using finger gesture and convert that into mask, But at the same time it should be real time, It should work as i move my finger across the grayscale image. Can anyone direct me to correct path.
Sample app : https://itunes.apple.com/us/app/color-splash/id304871603?mt=8
Thanks.
You can position two UIImageViews over each other, the color version in the background and the black&white version in the foreground.
Then you can use touchesBegan, touchesMoved and so on events to track user input. In touches moved you can "erase" a path that the user moved the finger along like this (self.foregroundDrawView is the black&white UIImageView):
UIGraphicsBeginImageContext(self.foregroundDrawView.frame.size);
[self.foregroundDrawView.image drawInRect:CGRectMake(0, 0, self.foregroundDrawView.frame.size.width, self.foregroundDrawView.frame.size.height)];
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetBlendMode(context, kCGBlendModeClear);
CGContextSetAllowsAntialiasing(context, TRUE);
CGContextSetLineWidth(context, 85);
CGContextSetLineCap(context, kCGLineCapRound);
CGContextSetRGBStrokeColor(context, 1, 0, 0, 1.0);
// Soft edge ... 5.0 works ok, but we try some more
CGContextSetShadowWithColor(context, CGSizeMake(0.0, 0.0), 13.0, [UIColor redColor].CGColor);
CGContextBeginPath(context);
CGContextMoveToPoint(context, touchLocation.x, touchLocation.y);
CGContextAddLineToPoint(context, currentLocation.x, currentLocation.y);
CGContextStrokePath(UIGraphicsGetCurrentContext());
self.foregroundDrawView.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
The important part is CGContextSetBlendMode(context, kCGBlendModeClear);. This erases the traced part from the image, afterwards the image is set back as the image of the foreground image view.
When the user is done you should be able to combine the two images or use the black&white image as a mask.

How don't clear UIView when I draw?

I'm drawing a line on UIView. When I call -setNeedsDisplay, my view becomes clear and drawing new line. How to continue current line? And how drawing with animation? Thanks.
- (void)drawRect:(CGRect)rect {
[super drawRect:rect];
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetStrokeColorWithColor(context, [_color CGColor]);
CGContextSetLineWidth(context, 10.0);
CGContextMoveToPoint(context, _startPointX, 0);
CGContextAddLineToPoint(context, _endPointX, 0);
CGContextStrokePath(context);
}
setNeedsDisplay totally redraw view. So you need to store all drawings somewhere and apply it every redraw.
What do you mean under animating drawing? Animating drawing is drawing itself. Just redraw the view after every small change and it will looks like you draw naturally.
For animation, you can create a CAShapeLayer with these lines.
Then create a CABasicAnimation with keypath #"storkeEnd"
toValue set #1 to draw all your lines from clear to drawn down.

Clipping a custom UIView to a triangle

First off, here's an image of my current situation.
To accomplish this I am using a subclassed UIView with the following method:
-(void)drawRect:(CGRect)rect
{
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextBeginPath(ctx);
CGContextMoveToPoint (ctx, CGRectGetMinX(rect), CGRectGetMinY(rect)); // top left
CGContextAddLineToPoint(ctx, CGRectGetMaxX(rect), CGRectGetMidY(rect)); // mid right
CGContextAddLineToPoint(ctx, CGRectGetMinX(rect), CGRectGetMaxY(rect)); // bottom left
CGContextClosePath(ctx);
CGContextSetRGBFillColor(ctx, 0, 1, 0, 1);
CGContextFillPath(ctx);
}
My ultimate goal is to clip the colored regions so that only the neon green remains. (I'll flip the colors later). I want it to look like a pie chart. I'm guessing there's a way to clip the colored corners out but I have no clue. CGContextClip(ctx) doesn't work.
I am using the Pixate framework as well. If anyone knows a way to accomplish a triangle shape with Pixate that would be even better.
The easiest way to clip a UIView to a path is to create a CAShapeLayer, add a path to the layer, and add the shape layer as a mask of your view's layer.
Or with a CGPathRef, essentially the same thing but at core foundation level
eg..
{
// before existing drawing code..
CGMutablePathRef clipTriangle = CGPathCreateMutable();
CGPathMoveToPoint (clipTriangle, nil, startX, startY);
CGPathAddLineToPoint(clipTriangle, nil, secondPointX, secondPointY);
CGPathAddLineToPoint(clipTriangle, nil, thirdPointX, thirdPointY);
CGPathCloseSubpath(clipTriangle);
CGContextSaveGstate(ctx);
CGContextAddPath(ctx, clipTriangle); /// this is the essential line I left out, we drew the triangle and forgot to use it, clipping to nothing instead, sorry mate
CGContextClip(ctx);
//draw stuff that you wanted clipped here...
CGContextRestoreGstate(ctx);
CGPathRelease(clippingTriangle); //CFstuff still needs manual memory management regardless of ARC
}

why CGContextSaveGState is not required even after several modification to the current context?

I am really strugggling with Quartz2D for more then 10 days please help me understand few concepts I will be really grateful, please look at this code and screenshot url.
This code draw image with border and write text to it and the image become whole new image with border and text.
//part 1
CGSize cgs = CGSizeMake(250.0, 400.0);
UIGraphicsBeginImageContext(cgs);
CGRect rectangle = CGRectMake(0,0,cgs.width,cgs.height);
CGRect imageRect = CGRectInset(rectangle, 5.4, 5.4);
imageRect.size.height -= 100;
UIImage *myImage = [UIImage imageNamed:#"BMW.jpg"];
[myImage drawInRect:imageRect];
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(context, 10.0);
CGContextSetRGBStrokeColor(context, 0.0, 0.0, 1.0, 1.0);
CGContextStrokeRect(context, rectangle);
//
//part 2
1. CGRect contextRect = rectangle;
2. CGContextTranslateCTM(context, 0, contextRect.size.height);
3. CGContextScaleCTM(context, 1, -1);
4. float w, h;
5. w = contextRect.size.width;
6. h = contextRect.size.height;
7. CGContextSelectFont (context, "Helvetica-Bold", 25,
kCGEncodingMacRoman);
8. CGContextSetCharacterSpacing (context, 5);
9. CGContextSetRGBFillColor(context, 0.0, 1.0, 1.0, 1.0);
10. CGContextSetRGBStrokeColor(context, 1.0, 0.0, 0.0, 1.0);
11. CGContextShowTextAtPoint(context, 45, 50, "Quartz 2D", 9);
//
//part 3
UIImage *testImg = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[testImg drawAtPoint:CGPointMake(35, 10)];
//
http://i40.tinypic.com/140aptv.png
part 1 and part 3 of the code is very clear to me
problem is regarding part 2
on line 2 and 3 coordinates are transformed so the text do not display
upside down, but uiimage already take care of this internally, why it
didn't transformed to upside down? why it is still displaying in
correct position after transform is applied for text using same
context? I am asking this because when uiimage coordinates are already
modified then this coordinate transform will not make uiimage again
upside down?
on line 9 and 10 fillcolor and strokecolor methods are called and
fillcolor changes the text color, but strokecolor not doing any thing
to text why? And why without CGContextSaveGState it modified the
color of text not the border color?
regarding these both points I mentioned above the common confusion is
why its working perfectly why this code didn't need
CGContextSaveGState and CGContextRestoreGState. How it is possible
that context is modified and it didn't effect the perviously drawing
item like blue border in this case and coordinates transformation for
text.
Please correct me if I am lacking in any way to make you understand my points.
Thanks in advance,
Regards.
Quartz 2D uses the "painter's model." That means, you draw one thing, and it's done. Then you draw another thing, and it goes on top of what you drew before. Then you draw another thing and that goes on top, etc. If I pick up a stamp, dip it in paint and press it to paper, then turn it over and do it again to another part of the paper, the first stamped image doesn't flip over just because I flipped the stamp.
Every time you see "stroke" or "draw," you're modifying the final image. Later changes to the context don't effect that.

Resources