Objective C: Touch Draw On UIScrollView - objective-c

I am trying to make a drawing book app with different pages on it using a pageEnabled UIScrollView.
What am i gonna use?? touch events are not working.
i have this code before on touches moved:
touchSwiped = YES;
UITouch *touch = [touches anyObject];
CGPoint currentPoint = [touch locationInView:self.view];
currentPoint.y -= 20;
UIGraphicsBeginImageContext(self.view.frame.size);
[drawView.image drawInRect:CGRectMake(0, 0, drawView.frame.size.width, drawView.frame.size.height)];
CGContextSetLineCap(UIGraphicsGetCurrentContext(), kCGLineCapRound);
CGContextSetLineWidth(UIGraphicsGetCurrentContext(), 15.0);
CGContextSetRGBStrokeColor(UIGraphicsGetCurrentContext(), 0, 0, 0, 1.0);
CGContextBeginPath(UIGraphicsGetCurrentContext());
CGContextMoveToPoint(UIGraphicsGetCurrentContext(), lastPoint.x, lastPoint.y);
CGContextAddLineToPoint(UIGraphicsGetCurrentContext(), currentPoint.x, currentPoint.y);
CGContextStrokePath(UIGraphicsGetCurrentContext());
drawView.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
lastPoint = currentPoint;
touchMoved++;
if (touchMoved == 10) {
touchMoved = 0;
}
i don't have any idea what adjustments i need to do in order to implement this.
anyone??
thanks in advance.

If you are using a UIPanGestureRecognizer as mentioned in your comments, then you have set the minimumNumberOfTouches to something greater than 1, right? Otherwise, how does it tell the difference between drawing and scrolling?
My recommendations are:
Do not use a UIScrollView unless you first figure out a way to conclusively determine if a touch is for drawing or for scrolling as intended by the user. Use a subclass of UIView instead, and then maybe use buttons to move between pages.
Or you could have a button that toggles between drawing and scrolling (paging) between the pages of your scroll view.
Check out this website for some tips and actual code to do the drawing http://www.ifans.com/forums/showthread.php?t=132024

Related

Clear CGContext Lines

Once I drawn some lines over an UIImage, (like a brush application), then I want to clear them, so like a rubber. I searched a lot on this site and on Google, and I tried some codes, but I couldn't find a correct solution.
Here's the code I use to write:
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
if(isDrawing == YES) {
UITouch *touch = [touches anyObject];
CGPoint currentPoint = [touch locationInView:mainImageView.superview];
UIGraphicsBeginImageContext(mainImageView.frame.size);
[mainImageView.image drawInRect:CGRectMake(0, 0, mainImageView.frame.size.width, mainImageView.frame.size.height)];
CGContextSetLineCap(UIGraphicsGetCurrentContext(), kCGLineCapRound);
CGContextSetLineWidth(UIGraphicsGetCurrentContext(), dimension);
const CGFloat *components = CGColorGetComponents([color CGColor]);
CGContextSetRGBStrokeColor(UIGraphicsGetCurrentContext(), components[0], components[1], components[2], components[3]);
CGContextBeginPath(UIGraphicsGetCurrentContext());
CGContextMoveToPoint(UIGraphicsGetCurrentContext(), lastPoint.x, lastPoint.y);
CGContextAddLineToPoint(UIGraphicsGetCurrentContext(), currentPoint.x, currentPoint.y);
CGContextStrokePath(UIGraphicsGetCurrentContext());
mainImageView.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
lastPoint = currentPoint;
} else {
//Clear current line (like a rubber would do)
}
}
In this scenario [UIColor clearColor] won't work, use CGContextClearRect(context,rect); to clear the region!
Fill desired part of the context with the [UIColor clearColor].
The correct approach is using a Bezier path that:
records every drawings ( but actually does NOT draw.. in DrawRect we will draw.. using "stroke")
has thickness, color and so on...
draws effectively on view
Clearing it (using "removeAllPoints") and invoking setNeedsDisplay will make the background reappear.
If You want to rubber-clear the path you wrote using another tool, (say You use a pencil tool to write in blue.. and a Rubber tool to clear the blue lines and make background to reappear) you can use "containsPoint:" to remove some points form bezier path.
We did in such way.
using bezier path is NOT trivial but very exciting.. :)

How can i improve arrowhead line drawing using CoreGraphics?

In my app i have view where user can draw arrows.
It works almost perfect.
If i swipe slowly i have nice result.
Image
Bu if i try to swipe faster i got the arrowhead somewhere but not at the very end of line.
image
Sorry, due to my reputation i can't post images here.
How Can i improve arrowhead line drawing to draw however fast i swipe?
To do this i use next method -
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
mouseSwiped = YES;
UITouch *touch = [touches anyObject];
CGPoint currentPoint = [touch locationInView:self.view];
UIGraphicsBeginImageContext(self.view.frame.size);
[self.tempDrawImage.image drawInRect:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
CGContextClearRect(UIGraphicsGetCurrentContext(),CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height));
double slopy, cosy, siny;
// Arrow size
double length = 10.0;
double width = 10.0;
slopy = atan2((firstPoint.y - lastMovePoint.y), (firstPoint.x - lastMovePoint.x));
cosy = cos(slopy);
siny = sin(slopy);
CGContextMoveToPoint(UIGraphicsGetCurrentContext(), firstPoint.x, firstPoint.y);
CGContextAddLineToPoint(UIGraphicsGetCurrentContext(), currentPoint.x, currentPoint.y);
//here is the tough part - actually drawing the arrows
//a total of 6 lines drawn to make the arrow shape
CGContextMoveToPoint(UIGraphicsGetCurrentContext(), lastMovePoint.x, lastMovePoint.y);
CGContextAddLineToPoint(UIGraphicsGetCurrentContext(),
lastMovePoint.x + (length * cosy - ( width / 2.0 * siny )),
lastMovePoint.y + (length * siny + ( width / 2.0 * cosy )) );
CGContextAddLineToPoint(UIGraphicsGetCurrentContext(),
lastMovePoint.x + (length * cosy + width / 2.0 * siny),
lastMovePoint.y - (width / 2.0 * cosy - length * siny) );
CGContextClosePath(UIGraphicsGetCurrentContext());
CGContextSetLineCap(UIGraphicsGetCurrentContext(), kCGLineCapRound);
CGContextSetLineWidth(UIGraphicsGetCurrentContext(), brush );
CGContextSetRGBStrokeColor(UIGraphicsGetCurrentContext(), red, green, blue, 1.0);
CGContextSetBlendMode(UIGraphicsGetCurrentContext(),kCGBlendModeNormal);
CGContextStrokePath(UIGraphicsGetCurrentContext());
self.tempDrawImage.image = UIGraphicsGetImageFromCurrentImageContext();
[self.tempDrawImage setAlpha:opacity];
UIGraphicsEndImageContext();
lastMovePoint = currentPoint;
From my experience when drawing with core graphics, when swiping fast over a view, the distance between the points every time touchesMoved: gets called is larger, and I see that you are using a lastMovePoint which can be further than you'd like, giving you unwanted results. This does not happen when moving slowly and the distance between each point is minimal (sometimes even less than 1 point)
I suggest working only with the firstPoint and currentPoint, calculate the angle and draw the arrow using only that, because, when it comes to it, to draw a line you only need the first and last point, it doesn't matter where the user touched before :)

Objective C: Received Memory Warning on ARC

I am having memory warning with my app then it crashes. The error, I think is on my draw-on-touch feature, because when I tried to run it with the alloc instrument the memory starts increasing when i keep on moving my finger on the screen.
Here's my code.
touchSwiped = YES;
UITouch *touch = [touches anyObject];
previousPoint2 = previousPoint1;
previousPoint1 = currentTouch;
currentTouch = [touch locationInView:self.view];
CGPoint mid1 = midPoint(previousPoint2, previousPoint1);
CGPoint mid2 = midPoint(currentTouch, previousPoint1);
UIGraphicsBeginImageContext(CGSizeMake(480 , 320));
[drawView.image drawInRect:CGRectMake(0, 0, 480,320)];
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetLineCap(context,kCGLineCapRound);
CGContextSetLineWidth(context, inkWidth);
CGContextSetBlendMode(context, blendMode);
CGContextSetRGBStrokeColor(context,redColor, greenColor, blueColor, 1);
CGContextBeginPath(context);
CGContextMoveToPoint(context, mid1.x, mid1.y);
CGContextAddQuadCurveToPoint(context, previousPoint1.x, previousPoint1.y, mid2.x, mid2.y);
CGContextStrokePath(context);
drawView.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsGetCurrentContext();
When using UIGraphicsBeginImageContext, you should always balance it with UIGraphicsEndImageContext after you're done using it or it won't be cleaned up neatly. Code running following your method would still believe that the current UI context is the one that you've released manually with potentially disastrous results.
I did what Ohr's said on his comment and it worked for me.
I released the CGContextRef like this CGContextRelease(context);

how to create curve for lines when handwriting

I'm creating an app, and in one part, I'm trying to implement hand-writing. I've used an imageView to write on, which will be saved and sent to a server as a pdf. I have implemented touch begin, move and end, and using contextAddLineToPoint, I can create the lines as user writes the stuff. However. The writing is a bit pointy, and I'm trying to create curves when user changes the direction of the writing, i.e. when a letter is written. I don't really want to implement hand-writing recognition, but just to smoothen the line being drawn.
I've created a "buffer", made up of an array, which holds two intermediate points when user moves the touch. as follow:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
lastPoint = [touch locationInView:drawImage];
NSLog(#"first touch:%#",[NSValue valueWithCGPoint:lastPoint]);
}
drawImage is the imageView I'm using to write on btw. Then goes to touch moved:
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
currentPoint = [touch locationInView:drawImage];
if (movementCount<2) {
[pointHolderArray addObject:[NSValue valueWithCGPoint:currentPoint]];
movementCount ++;
NSLog(#"pointHolderArray: %#",pointHolderArray);
}else
{
NSLog(#"buffer full");
UIGraphicsBeginImageContext(drawImage.frame.size);
[drawImage.image drawInRect:drawImage.frame];
CGContextSetLineCap(UIGraphicsGetCurrentContext(), kCGLineCapRound);
CGContextSetAllowsAntialiasing(UIGraphicsGetCurrentContext(), YES);
CGContextSetLineWidth(UIGraphicsGetCurrentContext(), 3.0);
CGContextSetRGBStrokeColor(UIGraphicsGetCurrentContext(), 0.3, 0.5, 0.2, 1.0);
CGContextBeginPath(UIGraphicsGetCurrentContext());
CGContextMoveToPoint(UIGraphicsGetCurrentContext(), lastPoint.x, lastPoint.y);
CGContextAddLineToPoint(UIGraphicsGetCurrentContext(),[[pointHolderArray objectAtIndex:0]CGPointValue].x,[[pointHolderArray objectAtIndex:0]CGPointValue].y);
CGContextMoveToPoint(UIGraphicsGetCurrentContext(), [[pointHolderArray objectAtIndex:0]CGPointValue].x,[[pointHolderArray objectAtIndex:0]CGPointValue].y);
CGContextAddLineToPoint(UIGraphicsGetCurrentContext(), [[pointHolderArray objectAtIndex:1]CGPointValue].x,[[pointHolderArray objectAtIndex:1]CGPointValue].y);
CGContextMoveToPoint(UIGraphicsGetCurrentContext(), [[pointHolderArray objectAtIndex:1]CGPointValue].x,[[pointHolderArray objectAtIndex:1]CGPointValue].y);
CGContextAddLineToPoint(UIGraphicsGetCurrentContext(), currentPoint.x, currentPoint.y);
CGContextStrokePath(UIGraphicsGetCurrentContext());
drawImage.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
lastPoint = [touch previousLocationInView:drawImage];
[pointHolderArray removeAllObjects];
movementCount=0;
}
}
As you can see, every time two points have been stored, I then draw the line between them. This has made the drawing slightly harder, and the line is even more ragged.
Can anyone help with the problem, I'm very new to graphics in iOS, and not sure if I'm even using the right API, and if I should even be using an imageView.
Thanks alot in advance
How about using Pan Gesture Recognizer and GLPaint. You set something like this in viewDidLoad:
- (void)viewDidLoad
{
[super viewDidLoad];
UIPanGestureRecognizer *g = [[UIPanGestureRecognizer alloc]initWithTarget:self action:#selector(viewPanned:)];
..
Then, capture the pan movement:
-(void) viewPanned:(UIPanGestureRecognizer *)g
{
CGPoint point = [g locationInView:paintView];
// invert y for OpenGL
point.y = paintView.bounds.size.height - point.y ;
switch (g.state) {
case UIGestureRecognizerStateBegan:
prevPoint = point ;
break ;
case UIGestureRecognizerStateChanged:
[paintView renderLineFromPoint:prevPoint toPoint:point] ;
prevPoint = point ;
break ;
case UIGestureRecognizerStateEnded:
prevPoint = point ;
break ;
}
}
The 'paintView' shown here is the instance of PaintView which you can find in GLPaint example in Apple sample code. The example does not show how to change the pen size, but you can do it by setting various glPointSize.
- (void)setBrushSize:(CGFloat)size
{
if( size <= 1.0 ) {
glPointSize(10);
}
else if ( size <= 2.0 ) {
glPointSize(20);
}
else if ( size <= 3.0 ) {
glPointSize(30);
}
else if ( size <= 4.0 ) {
glPointSize(40);
}
..
Hope this helps..

Draw and Erase Lines with Touch

I have a background image (ImageBG) and a foreground image (ImageFG) in separate UIImageViews. They are the same size and, as the name implies, ImageBG is behind ImageFG so you can't see ImageBG.
If the user starts to "draw" on them, instead of a line appearing where the user touches the screen, I want that portion of ImageFG to become transparent and reveal ImageBG.
The closest thing I can think of is a scratch-and-win.
I know how to do the drawing on a graphics context, but when I draw a transparent line (alpha = 0), well... I get a transparent line on top of whatever is currently in my graphics context, so basically nothing is drawn. This is what I use to set the stroke color.
CGContextSetRGBStrokeColor(UIGraphicsGetCurrentContext(), 1.0, 0.0, 0.0, 0.0)
What am I doing wrong? I know this is possible because there are apps out there that do it.
Any hints, tips or direction is appreciated.
So you have a layer that is some image like the grey stuff you want to scratch off, you write this image (in this example scratchImage which is a UIImageView and its UIImage .image )to the graphics context. You then stroke it with the blend mode set to kCGBlendModeClear then you save that image (with the cleared paths). This will reveal an image that you have underneath perhaps in another UIImageView. Note that in this instance, self is a UIView.
So outside touchesMoved create a variable to hold a CGPoint
CGPoint previousPoint;
CGPoint currentPoint;
Then in touchesBegan, set it to the previous point.
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
previousPoint = [touch locationInView:self];
}
In touchesMoved, write the image to the context, stroke the image with the clear blend mode, save the image from the context, and set the previous point to the current point.
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
currentPoint = [touch locationInView:self];
UIGraphicsBeginImageContext(self.frame.size);
//This may not be necessary if you are just erasing, in my case I am
//adding lines repeatedly to an image so you may want to experiment with moving
//this to touchesBegan or something. Which of course would also require the begin
//graphics context etc.
[scratchImage.image drawInRect:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];
CGContextSaveGState(UIGraphicsGetCurrentContext());
CGContextSetShouldAntialias(UIGraphicsGetCurrentContext(), YES);
CGContextSetLineCap(UIGraphicsGetCurrentContext(), kCGLineCapRound);
CGContextSetLineWidth(UIGraphicsGetCurrentContext(), 5.0);
CGContextSetRGBStrokeColor(UIGraphicsGetCurrentContext(), 0.25, 0.25, 0.25, 1.0);
CGMutablePathRef path = CGPathCreateMutable();
CGPathMoveToPoint(path, nil, previousPoint.x, previousPoint.y);
CGPathAddLineToPoint(path, nil, currentPoint.x, currentPoint.y);
CGContextSetBlendMode(UIGraphicsGetCurrentContext(), kCGBlendModeClear);
CGContextAddPath(UIGraphicsGetCurrentContext(), path);
CGContextStrokePath(UIGraphicsGetCurrentContext());
scratchImage.image = UIGraphicsGetImageFromCurrentImageContext();
CGContextRestoreGState(UIGraphicsGetCurrentContext());
UIGraphicsEndImageContext();
previousPoint = currentPoint;
}