I have got a question about quartz core on iOS
When I use Quartz to draw lines, the following happens and I can't find the reason:
when I draw the second line the first line would disappear
when I draw the third line the second would the disappear, and the 1st and 3rd would show
..
when I draw the 2n+1th line, 1,3,5,...2n-1th lines shows, and 2,4,6,8... 2n dissappears
see the code below. I don't save contexts and paths
as my understanding, I think it should be one of the two cases
display all lines I drawn
display the last line I drawn and the previous lines should disappear
But the two cases don't happen.
- (void)drawInContext:(CGContextRef)context {
// 橡皮擦
//CGContextClearRect(context, CGRectMake(0, 0, 320, 480));
CGContextSetLineWidth(context, 4.0);
CGContextMoveToPoint(context, previousPoint.x, previousPoint.y);
CGContextSetRGBStrokeColor(context, 0, 1.0, 1.0, 1.0);
CGContextAddLineToPoint(context, nextPoint.x,nextPoint.y);
CGContextStrokePath(context);
//NIF_TRACE(#"began : %# moved : %#", NSStringFromCGPoint(previousPoint),NSStringFromCGPoint(nextPoint));
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
previousPoint = [touch locationInView:self];
nextPoint = [touch locationInView:self];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
previousPoint = nextPoint;
nextPoint = [touch locationInView:self];
[self setNeedsDisplay];
}
I saw a demo that use NSMutableArray to save all UIBezierPaths it drawn, when the View redraw, it transfer paths saved in array, restores them in drawRect:
UIBezierPath is a wrapper of Objective C, and it only works in 3.2+
I need do something make it work in 3.0+
I think it must be exists a better method to save contexts and paths (Colors,Paths,strokeWidths)
Anybody have ideas?
Every time you "drawInContext" you are clearing the drawing area and placing the new line.... This clear code:
CGContextClearRect(context, CGRectMake(0, 0, 320, 480));
Needs to be somewhere else in your code... somewhere where it will only be activated 1 time (or if you need to clear the whole drawing (perhaps an erase all function))
You need to save the image on canvas:
CGImageRef imageRef;
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
if (imageRef) {
// Restore the screen that was previously saved
CGContextTranslateCTM(context, 0, rect.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
CGContextDrawImage(context, rect, imageRef);
CGImageRelease(imageRef);
CGContextTranslateCTM(context, 0, rect.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
}
// Your drawing code here...
imageRef = CGBitmapContextCreateImage(context);
}
Related
I am looking for a way of being able to erase a UIImageView from the screen. When I say erase I don't mean [imageView removeFromSuperview];, I mean to erase parts of the image by scribbling your finger on the screen. Where ever your finger is, that is the portion of the image that is erased. I just can't find any help with this.
I would image in has to do with Quartz? If so, I'm not real good with that. :(
I guess the best example is a Lottery ticket. Once you scratch the portion of the ticket, that area beneath it reveals. Anyone know how to accomplish this?
Thank you!
Update: The following code is what did the trick. Thank you!
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
lastTouch = [touch locationInView:canvasView];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
currentTouch = [touch locationInView:canvasView];
CGFloat brushSize = 35;
CGColorRef strokeColor = [UIColor whiteColor].CGColor;
UIGraphicsBeginImageContext(scratchView.frame.size);
CGContextRef context = UIGraphicsGetCurrentContext();
[canvasView.image drawInRect:CGRectMake(0, 0, canvasView.frame.size.width, canvasView.frame.size.height)];
CGContextSetLineCap(context, kCGLineCapRound);
CGContextSetLineWidth(context, brushSize);
CGContextSetStrokeColorWithColor(context, strokeColor);
CGContextSetBlendMode(context, kCGBlendModeClear);
CGContextBeginPath(context);
CGContextMoveToPoint(context, lastTouch.x, lastTouch.y);
CGContextAddLineToPoint(context, currentTouch.x, currentTouch.y);
CGContextStrokePath(context);
canvasView.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
lastTouch = [touch locationInView:canvasView];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
}
You can definitely do this with a UIImageView, no custom Quartz layer required. Are you familiar with any form of drawing in iOS? Basically, you just need to keep track of the current and previous touch location using touchesBegan:, touchesMoved:, and touchesEnded.
Then you need to draw a 'line' (which in this case erases what's underneath it) between the current touch location and previous touch location using something like the following, which is taken directly from an actual application I developed that did something rather similar:
UIGraphicsBeginImageContext(canvasView.frame.size);
CGContextRef context = UIGraphicsGetCurrentContext();
[canvasView.image drawInRect:CGRectMake(0, 0, canvasView.frame.size.width, canvasView.frame.size.height)];
CGContextSetLineCap(context, lineCapType);
CGContextSetLineWidth(context, brushSize);
CGContextSetStrokeColorWithColor(context, strokeColor);
CGContextSetBlendMode(context, kCGBlendModeClear);
CGContextBeginPath(context);
CGContextMoveToPoint(context, lastTouch.x, lastTouch.y);
CGContextAddLineToPoint(context, currentTouch.x, currentTouch.y);
CGContextStrokePath(context);
canvasView.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
In this code canvasView is a UIImageView. There are lots of tutorials out there for this kind of drawing. The important thing for what you want is to set the blend mode to kCGBlendModeClear. That's this line:
CGContextSetBlendMode(context, kCGBlendModeClear);
I want to overlay a a box shape over a photograph and allow the user to select each corner, and drag the corners to where they want.
I could use 4 invisible buttons(to represent each corner) that respond to drag events to get the x,y points for each corner, but is there some line drawing functionality available in xcode without touching any of the game api classes? I guess I want to draw lines onto a UIView.
Many Thanks,
-Code
Make a subclass of UIView to represent your view. Add a UIImageView to your view. This will hold the image with user's drawing.
Enable user interaction in the UIView sub-class.
self.userInteractionEnabled = YES;
Detect the starting tap by implementing this method in your subclass:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
// We are starting to draw
// Get the current touch.
UITouch *touch = [touches anyObject];
startingPoint = [touch locationInView:self];
}
Detect the last tap to draw straight line:
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
endingPoint = [touch locationInView:self];
// Now draw the line and save to your image
UIGraphicsBeginImageContext(self.frame.size);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(context, 10);
CGContextMoveToPoint(context, NULL, startingPoint.x, startingPoint.y);
CGContextAddLineToPoint(context, NULL, endingPoint.x, endingPoint.y);
CGContextSetRGBFillColor(context, 255, 255, 255, 1);
CGContextSetRGBStrokeColor(context, 255, 255, 255, 1);
CGContextStrokePath(context);
self.image = UIGraphicsGetImageFromCurrentImageContext();
CGContextRelease(context);
}
Hi I want to implement a very simple game on iphone. I have a image and a text. If a user draw a line between the image and the text then it says "correct"
Now what I am thinking is that whenever a user touch the screen I will get the location (like X,Y) of that touching spot ( which method to call to get the location? ) and then I will draw a black dot there. If the user continuously and smoothly touch the screen then a line will form. Finally I will only need to judge whether the starting point of the line locates within the image and the ending point locates within the text.
I dont know whether my idea is a correct way to implement this kinda game. If it is how to get coordinate of touching spot and draw a black dot in that specific location?
Thanks for helping!
You may get the touch coordinates in touchesMoved of your view controller:
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint touch_point = [touch locationInView:self.view];
NSLog(#"x: %.0f y: %.0f", touch_point.x, touch_point.y);
}
To draw the line, you have add a view and draw line in drawRect:
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(context, 1.0);
CGContextSetStrokeColorWithColor(context, [UIColor orangeColor].CGColor);
// ...
CGContextMoveToPoint(context, one_x, one_y);
CGContextAddLineToPoint(context, two_x, two_y);
CGContextStrokePath(context);
// ...
}
where one_x one_y two_x two_x are coordinates for two points ..
This is exactly how the iPhone touch delegation works. It calls touchesBegan:, touchesMoved: and touchesEnded:. To draw the lines, you need this code:
CGContextRef context = UIGraphicsGetCurrentContext();
[current set];
CGContextSetLineWidth(context, 4.0f);
for (int i = 0; i < (self.points.count - 1); i++)
{
CGPoint pt1 = POINT(i);
CGPoint pt2 = POINT(i+1);
CGContextMoveToPoint(context, pt1.x, pt1.y);
CGContextAddLineToPoint(context, pt2.x, pt2.y);
CGContextStrokePath(context);
}
And with the help of this code, you should be ready to make it. If you need any help, just comment on this or make another question.
CGPoint pt = [[touches anyObject] locationInView:self];
[self.points addObject:[NSValue valueWithCGPoint:pt]];
[self setNeedsDisplay];
How can I draw a line between one point (the center of one UIView) to a point that moves (touch location), and the line moves the 2nd point as the touch moves.
In your custom view:
in touchesMoved:withEvent store current point into a variable, and call [self setNeedsDisplay] so that the view would redraw
implement drawing of a line in drawRect:, use core graphics to draw a line
Let's say you store the touched point into property self.touchedPoint, then drawing might look like this:
#property (nonatomic, assign) CGPoint touchedPoint;
- (void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);
CGContextTranslateCTM(context, 0.0, rect.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
CGContextSetShouldAntialias(context, YES);
CGContextSetLineWidth(context, 1.0f);
CGContextSetRGBStrokeColor(context, 0.7, 0.7, 0.7, 1.0);
CGContextMoveToPoint(context, rect.size.width/2, rect.size.height/2);
CGContextAddLineToPoint(context, self.touchedPoint.x, self.touchedPoint.y);
CGContextDrawPath(context, kCGPathStroke);
CGContextRestoreGState(context);
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
self.touchedPoint = [[touches anyObject] locationInView:self];
[self setNeedsDisplay];
}
I voted Michal's answer up. But I would also suggest looking at the Touches sample project. It is easy to get it running - which may be helpful if you are still just putting together your project.
I'm basically looking to create something akin to a very simple version of iSteam/iFog alebit for a different purpose. In effect there will be two images, one of the subject matter and the other an image of condensation or some such. The user can then wipe their finger over the screen and it will "cut" that from the top layer to reveal the lower layer. So far I've been able to draw a basic line on the screen using CoreGraphics and strokes but I can't find a way to then use this as an alpha mask for the steam layer.
If anyone could give me advice on what to use or even better some sample code I'd be very grateful as right now I'm pulling my hair out. Here is what I have so far:
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
mouseSwiped = YES;
UITouch *touch = [touches anyObject];
CGPoint currentPoint = [touch locationInView:self.view];
UIGraphicsBeginImageContext(self.view.frame.size);
CGRect rect = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height);
[drawImage.image drawInRect:rect];
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetLineCap(context, kCGLineCapRound);
CGContextSetLineWidth(context, 36.0);
CGContextSetRGBStrokeColor(context, 1.0, 1.0, 1.0, 1.0);
CGContextBeginPath(context);
CGContextMoveToPoint(context, lastPoint.x, lastPoint.y);
CGContextAddLineToPoint(context, currentPoint.x, currentPoint.y);
CGContextStrokePath(context);
drawImage.image = UIGraphicsGetImageFromCurrentImageContext();
}
The method you want to call is CGContextClipToMask. Just draw your "steam" image and then the stroke on another CGImage. Then, clip the steam to the stroke. Something like this:
- (void)somewhereElse{
UIImage *steam = [[UIImage imageNamed:#"steamImage.png"] retain];
steamRef = steam.CGImage;
//...
}
- (void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextDrawImage(context, self.bounds, steamRef); //draw the main image
CGContextClipToMask(context, self.bounds, maskRef); //respect alpha mask
//where maskRef is a GCImage of your stroked path
}