Im developing a colouring app, however I cant manage to implement an UNDO button. I am unsure of the approach, I have tried implementing NSUndoManager, but I could not get it to work effectively. My approach is possibly incorrect. Im would greatly appreciate an answer that uses code, based on my example.
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
mouseSwiped = NO;
UITouch *touch = [touches anyObject];
lastPoint = [touch locationInView:self.view];
}
- (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.tempImage.image drawInRect:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
//get the current touch point and then draw a line with CGContextAddLineToPoint from lastPoint to currentPoint. You’re right to think that this approach will produce a series of straight lines, but the lines are sufficiently short that the result looks like a nice smooth curve.
CGContextMoveToPoint(UIGraphicsGetCurrentContext(), lastPoint.x, lastPoint.y);
CGContextAddLineToPoint(UIGraphicsGetCurrentContext(), currentPoint.x, currentPoint.y);
//Now set our brush size and opacity and brush stroke color:
CGContextSetLineCap(UIGraphicsGetCurrentContext(), kCGLineCapRound);
CGContextSetLineWidth(UIGraphicsGetCurrentContext(), brush );
CGContextSetRGBStrokeColor(UIGraphicsGetCurrentContext(), red, green, blue, 1.0);
CGContextSetBlendMode(UIGraphicsGetCurrentContext(),kCGBlendModeNormal);
//Finish it off by drawing the path:
CGContextStrokePath(UIGraphicsGetCurrentContext());
self.tempImage.image = UIGraphicsGetImageFromCurrentImageContext();
[self.tempImage setAlpha:opacity];
UIGraphicsEndImageContext();
lastPoint = currentPoint;
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
/*
check if mouse was swiped. If it was, then it means touchesMoved was called and you don’t need to draw any further. However, if the mouse was not swiped, then it means user just tapped the screen to draw a single point. In that case, just draw a single point.
*/
if(!mouseSwiped) {
UIGraphicsBeginImageContext(self.view.frame.size);
[self.tempImage.image drawInRect:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
CGContextSetLineCap(UIGraphicsGetCurrentContext(), kCGLineCapRound);
CGContextSetLineWidth(UIGraphicsGetCurrentContext(), brush);
CGContextSetRGBStrokeColor(UIGraphicsGetCurrentContext(), red, green, blue, opacity);
CGContextMoveToPoint(UIGraphicsGetCurrentContext(), lastPoint.x, lastPoint.y);
CGContextAddLineToPoint(UIGraphicsGetCurrentContext(), lastPoint.x, lastPoint.y);
CGContextStrokePath(UIGraphicsGetCurrentContext());
CGContextFlush(UIGraphicsGetCurrentContext());
self.tempImage.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}
UIGraphicsBeginImageContext(self.mainImage.frame.size);
[self.mainImage.image drawInRect:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height) blendMode:kCGBlendModeNormal alpha:1.0];
[self.tempImage.image drawInRect:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height) blendMode:kCGBlendModeNormal alpha:opacity];
//Once the brush stroke is done, merge the tempDrawImage with mainImage,
self.mainImage.image = UIGraphicsGetImageFromCurrentImageContext();
self.tempImage.image = nil;
UIGraphicsEndImageContext();
}
There has been similar questions asked but no clear answer was given, and a few left unanswered.
* EDIT 1 based on gabbler answer *
#import "ViewController.h"
#interface ViewController ()
#property (weak, nonatomic) IBOutlet UIImageView *mainImage;
#property (weak, nonatomic) IBOutlet UIImageView *tempImage;
#property (weak, nonatomic) IBOutlet UIImageView *palette;
#property (nonatomic) UIImage * previousImage;
#property (nonatomic,readonly) NSUndoManager * undoManager;
- (IBAction)colorPressed:(id)sender;
- (IBAction)erase:(id)sender;
- (IBAction)undo:(id)sender;
#end
#implementation ViewController
//gabbler code
- (void)setImage:(UIImage*)currentImage fromImage:(UIImage*)preImage
{
// Prepare undo-redo
[[self.undoManager prepareWithInvocationTarget:self] setImage:preImage fromImage:currentImage];
self.mainImage.image = currentImage;
self.tempImage.image = currentImage;
self.previousImage = currentImage;
}
- (IBAction)trash:(id)sender {
self.mainImage.image = nil;
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
red = 0.0/255.0;
green = 0.0/255.0;
blue = 0.0/255.0;
brush = 10.0;
opacity = 1.0;
//gabbler code
self.previousImage = self.tempImage.image;
}
- (IBAction)erase:(id)sender {
//set it to background color
red = 255.0/255.0;
green = 255.0/255.0;
blue = 255.0/255.0;
opacity = 1.0;
}
//gabbler code
- (IBAction)undo:(id)sender {
[self.undoManager undo];
}
#pragma mark - Touches
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
mouseSwiped = NO;
UITouch *touch = [touches anyObject];
lastPoint = [touch locationInView:self.view];
}
- (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.tempImage.image drawInRect:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
//get the current touch point and then draw a line with CGContextAddLineToPoint from lastPoint to currentPoint. You’re right to think that this approach will produce a series of straight lines, but the lines are sufficiently short that the result looks like a nice smooth curve.
CGContextMoveToPoint(UIGraphicsGetCurrentContext(), lastPoint.x, lastPoint.y);
CGContextAddLineToPoint(UIGraphicsGetCurrentContext(), currentPoint.x, currentPoint.y);
//Now set our brush size and opacity and brush stroke color:
CGContextSetLineCap(UIGraphicsGetCurrentContext(), kCGLineCapRound);
CGContextSetLineWidth(UIGraphicsGetCurrentContext(), brush );
CGContextSetRGBStrokeColor(UIGraphicsGetCurrentContext(), red, green, blue, 1.0);
CGContextSetBlendMode(UIGraphicsGetCurrentContext(),kCGBlendModeNormal);
//Finish it off by drawing the path:
CGContextStrokePath(UIGraphicsGetCurrentContext());
self.tempImage.image = UIGraphicsGetImageFromCurrentImageContext();
[self.tempImage setAlpha:opacity];
UIGraphicsEndImageContext();
lastPoint = currentPoint;
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
/*
check if mouse was swiped. If it was, then it means touchesMoved was called and you don’t need to draw any further. However, if the mouse was not swiped, then it means user just tapped the screen to draw a single point. In that case, just draw a single point.
*/
if(!mouseSwiped) {
UIGraphicsBeginImageContext(self.view.frame.size);
[self.tempImage.image drawInRect:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
CGContextSetLineCap(UIGraphicsGetCurrentContext(), kCGLineCapRound);
CGContextSetLineWidth(UIGraphicsGetCurrentContext(), brush);
CGContextSetRGBStrokeColor(UIGraphicsGetCurrentContext(), red, green, blue, opacity);
CGContextMoveToPoint(UIGraphicsGetCurrentContext(), lastPoint.x, lastPoint.y);
CGContextAddLineToPoint(UIGraphicsGetCurrentContext(), lastPoint.x, lastPoint.y);
CGContextStrokePath(UIGraphicsGetCurrentContext());
CGContextFlush(UIGraphicsGetCurrentContext());
self.tempImage.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}
UIGraphicsBeginImageContext(self.mainImage.frame.size);
[self.mainImage.image drawInRect:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height) blendMode:kCGBlendModeNormal alpha:1.0];
[self.tempImage.image drawInRect:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height) blendMode:kCGBlendModeNormal alpha:opacity];
//Once the brush stroke is done, merge the tempDrawImage with mainImage,
self.mainImage.image = UIGraphicsGetImageFromCurrentImageContext();
self.tempImage.image = nil;
UIGraphicsEndImageContext();
//gabbler code
UIImage *currentImage = UIGraphicsGetImageFromCurrentImageContext();
[self setImage:currentImage fromImage:currentImage];
}
#end
UIViewController's superclass UIResponder has a NSUndoManager property, which can be used here. The above code performs a setting image action for the imageView, to undo the action, you have to keep a reference of the previous image that was set to the imageView. You can make an instance variable called previousImage, in viewDidLoad, set
previousImage = self.tempImage.image;
And here is the function to set image for UIImageView.
- (void)setImage:(UIImage*)currentImage fromImage:(UIImage*)preImage
{
// Prepare undo-redo
[[self.undoManager prepareWithInvocationTarget:self] setImage:preImage fromImage:currentImage];
self.mainImage.image = currentImage;
self.tempImage.image = currentImage;
previousImage = currentImage;
}
In touchesEnded.
UIImage *currentImage = UIGraphicsGetImageFromCurrentImageContext();
[self setImage:currentImage fromImage:previousImage];
When you click a button for undoing.
- (IBAction)btnClicked:(id)sender {
[self.undoManager undo];
}
Edit
The above method will cause memory issues, don't save images in undoManager, save the line path points instead, here is a sample project with undo and redo capabilities.
Related
I am developing some functionality that will let the user "draw" on an image by placing their finger on the screen and moving it around. I have the following code (taken from the tutorial https://www.raywenderlich.com/18840/how-to-make-a-simple-drawing-app-with-uikit):
// Draws a line from point1 to point2
- (void) drawOnImage:(CGPoint *)point1 :(CGPoint *)point2 {
UIGraphicsBeginImageContext(self.view.frame.size);
[self.mainImage.image drawInRect:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
CGContextMoveToPoint(UIGraphicsGetCurrentContext(), point1->x, point1->y);
CGContextAddLineToPoint(UIGraphicsGetCurrentContext(), point2->x, point2->y);
CGContextSetLineCap(UIGraphicsGetCurrentContext(), kCGLineCapRound);
CGContextSetLineWidth(UIGraphicsGetCurrentContext(), brush );
CGContextSetRGBStrokeColor(UIGraphicsGetCurrentContext(), red, green, blue, 1.0);
CGContextSetBlendMode(UIGraphicsGetCurrentContext(),kCGBlendModeNormal);
CGContextStrokePath(UIGraphicsGetCurrentContext());
self.mainImage.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}
// Begining of the drawing
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
lastPoint = [touch locationInView:self.view];
if (currentAction == DRAW) {
[self backupImage];
mouseSwiped = NO;
//disable the renderImageView so that the gestures dont interfere
[self drawOnImage :&lastPoint :&lastPoint];
}
}
// When lines are being drawn on the image
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
if (currentAction == DRAW) {
mouseSwiped = YES;
UITouch *touch = [touches anyObject];
CGPoint currentPoint = [touch locationInView:self.view];
UIGraphicsBeginImageContext(self.view.frame.size);
[self.mainImage.image drawInRect:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)]; // I switched the tempDrawImage to mainImage
CGContextMoveToPoint(UIGraphicsGetCurrentContext(), lastPoint.x, lastPoint.y);
CGContextAddLineToPoint(UIGraphicsGetCurrentContext(), currentPoint.x, currentPoint.y);
CGContextSetLineCap(UIGraphicsGetCurrentContext(), kCGLineCapRound);
CGContextSetLineWidth(UIGraphicsGetCurrentContext(), brush );
CGContextSetRGBStrokeColor(UIGraphicsGetCurrentContext(), red, green, blue, 1.0);
CGContextSetBlendMode(UIGraphicsGetCurrentContext(),kCGBlendModeNormal);
CGContextStrokePath(UIGraphicsGetCurrentContext());
self.mainImage.image = UIGraphicsGetImageFromCurrentImageContext(); // I switched the tempDrawImage to mainImage
UIGraphicsEndImageContext();
lastPoint = currentPoint;
}
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
if(!mouseSwiped) {
UIGraphicsBeginImageContext(self.view.frame.size);
[self.mainImage.image drawInRect:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)]; // I switched the tempDrawImage to mainImage
CGContextSetLineCap(UIGraphicsGetCurrentContext(), kCGLineCapRound);
CGContextSetLineWidth(UIGraphicsGetCurrentContext(), brush);
CGContextSetRGBStrokeColor(UIGraphicsGetCurrentContext(), red, green, blue, 1);
CGContextMoveToPoint(UIGraphicsGetCurrentContext(), lastPoint.x, lastPoint.y);
CGContextAddLineToPoint(UIGraphicsGetCurrentContext(), lastPoint.x, lastPoint.y);
CGContextStrokePath(UIGraphicsGetCurrentContext());
CGContextFlush(UIGraphicsGetCurrentContext());
self.mainImage.image = UIGraphicsGetImageFromCurrentImageContext(); // I switched the tempDrawImage to mainImage
UIGraphicsEndImageContext();
}
if (currentAction == DRAW) {
UIGraphicsBeginImageContext(self.mainImage.frame.size);
[self.mainImage.image drawInRect:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height) blendMode:kCGBlendModeNormal alpha:1.0];
[self.mainImage.image drawInRect:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height) blendMode:kCGBlendModeNormal alpha:1]; // I switched the tempDrawImage to mainImage
self.mainImage.image = UIGraphicsGetImageFromCurrentImageContext();
//self.tempDrawImage.image = nil;
UIGraphicsEndImageContext();
}
}
If the screen is being touched very lightly when drawing sometimes the drawing stops. Like midway through a stroke the line stops drawing and in order to start drawing again you have to lift your finger off the screen and press again. This confuses me a great deal because I would think that if the touch gesture was not being recognized for a second then the drawing would stop for a brief second, and when it was recognized again the drawing would continue. This isn't what happens though, you have to lift your finger off the screen and re-apply it. Does anyone have any ideas on what could cause this or have any ideas on how to fix it?
I am using this raywenderlich tutorial for the drawing on Image. That tutorial is with no image in background but here I want to draw on image from gallery.
So basically there are two UIImageView one is mainImage and tempDrawImage.
I am doing self.mainImage.image = randomImageFromGallery as a background image on which I want to draw
Now it shows up well n drawing also happens but the problem is image gets scale on every touch end.
Why the image keeps on scaling whats wrong with the code I am not doing anything on tempDrawImage where all the drawing happens ?
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
mouseSwiped = NO;
UITouch *touch = [touches anyObject];
lastPoint = [touch locationInView:self.view];
UIGraphicsBeginImageContext(self.view.frame.size);
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
mouseSwiped = YES;
UITouch *touch = [touches anyObject];
CGPoint currentPoint = [touch locationInView:self.view];
CGContextRef ctxt = UIGraphicsGetCurrentContext();
CGContextMoveToPoint(ctxt, lastPoint.x, lastPoint.y);
CGContextAddLineToPoint(ctxt, currentPoint.x, currentPoint.y);
CGContextSetLineCap(ctxt, kCGLineCapRound);
CGContextSetLineWidth(ctxt, brush );
CGContextSetRGBStrokeColor(ctxt, red, green, blue, 1.0);
CGContextSetBlendMode(ctxt,kCGBlendModeNormal);
CGContextStrokePath(ctxt);
self.tempDrawImage.image = UIGraphicsGetImageFromCurrentImageContext();
[self.tempDrawImage setAlpha:opacity];
lastPoint = currentPoint;
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesCancelled:touches withEvent:event];
UIGraphicsEndImageContext();
if(!mouseSwiped)
{
self.tempDrawImage.image = nil;
}
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
if(!mouseSwiped) {
UIGraphicsEndImageContext();
UIGraphicsBeginImageContext(self.view.frame.size);
[self.tempDrawImage.image drawInRect:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
CGContextSetLineCap(UIGraphicsGetCurrentContext(), kCGLineCapRound);
CGContextSetLineWidth(UIGraphicsGetCurrentContext(), brush);
CGContextSetRGBStrokeColor(UIGraphicsGetCurrentContext(), red, green, blue, opacity);
CGContextMoveToPoint(UIGraphicsGetCurrentContext(), lastPoint.x, lastPoint.y);
CGContextAddLineToPoint(UIGraphicsGetCurrentContext(), lastPoint.x, lastPoint.y);
CGContextStrokePath(UIGraphicsGetCurrentContext());
CGContextFlush(UIGraphicsGetCurrentContext());
self.tempDrawImage.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}
UIGraphicsBeginImageContext(self.mainImage.frame.size);
[self.mainImage.image drawInRect:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height) blendMode:kCGBlendModeNormal alpha:1.0];
[self.tempDrawImage.image drawInRect:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height) blendMode:kCGBlendModeNormal alpha:opacity];
self.mainImage.image = UIGraphicsGetImageFromCurrentImageContext();
self.tempDrawImage.image = nil;
UIGraphicsEndImageContext();
}
Check the size of the frames of all of the views you're using. Somewhere there is a mismatch so that one of the images your displaying is being scaled for display. Either the size the image is drawn at or the size it's displayed at needs to be changed.
If I understand well, you have a picture, and you want to draw up to this picture...
I propose :
set your tempDrawImage as inherited from UIView instead of UIImageView.
Store the points in an array while touches actions (touchesBegan, touchesMoved,...)
draw in the procedure (void)drawRect:(CGRect)rect
I am following the tutorial to create a simple drawing pad app (http://www.raywenderlich.com/18840/how-to-make-a-simple-drawing-app-with-uikit#comments)
The app has a single UIViewController and a UIImageView in it. After implementing touchBegin, touchMoved, and touchEnd, I could draw but after every stroke ends, the whole picture moves up. So after several strokes, the whole picture will be distorted and squeezed to the very top of the screen. Any suggestions? Thank you very much.
Here is the code:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
self.mouseSwiped = NO;
UITouch *touch = [touches anyObject];
self.lastPoint =[touch locationInView:self.view];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
self.mouseSwiped = YES;
//find out the position where the touch event happened
UITouch *touch = [touches anyObject];
CGPoint currentPoint = [touch locationInView:self.view];
//Setup the drawing context and prepare to draw something
UIGraphicsBeginImageContext(self.view.frame.size);
//Create a drawing area that is of the same size as the view dimensions
[self.mainImage.image drawInRect:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
//Set the drawing starting point
CGContextMoveToPoint(UIGraphicsGetCurrentContext(), self.lastPoint.x, self.lastPoint.y);
//Setup a line from the starting point to specified point, i.e current point in this case
CGContextAddLineToPoint(UIGraphicsGetCurrentContext(), currentPoint.x, currentPoint.y);
//Set the drawing style
CGContextSetLineCap(UIGraphicsGetCurrentContext(), kCGLineCapRound);
CGContextSetLineWidth(UIGraphicsGetCurrentContext(), self.brush);
CGContextSetRGBStrokeColor(UIGraphicsGetCurrentContext(), self.red, self.green, self.blue, 1);
CGContextSetBlendMode(UIGraphicsGetCurrentContext(), kCGBlendModeNormal);
//Paint the line
CGContextStrokePath(UIGraphicsGetCurrentContext());
self.mainImage.image=UIGraphicsGetImageFromCurrentImageContext();
[self.mainImage setAlpha:self.opacity];
UIGraphicsEndImageContext();
self.lastPoint = currentPoint;
}
- (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
if(!self.mouseSwiped){
UIGraphicsBeginImageContext(self.view.frame.size);
[self.mainImage.image drawInRect:
CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
CGContextSetLineCap(UIGraphicsGetCurrentContext(), kCGLineCapRound);
CGContextSetLineWidth(UIGraphicsGetCurrentContext(), self.brush);
CGContextSetRGBStrokeColor(UIGraphicsGetCurrentContext(),
self.red, self.green, self.blue, self.opacity);
CGContextMoveToPoint(UIGraphicsGetCurrentContext(), self.lastPoint.x, self.lastPoint.y);
CGContextAddLineToPoint(UIGraphicsGetCurrentContext(), self.lastPoint.x, self.lastPoint.y);
CGContextStrokePath(UIGraphicsGetCurrentContext());
CGContextFlush(UIGraphicsGetCurrentContext());
self.mainImage.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}
UIGraphicsBeginImageContext(self.mainImage.frame.size);
[self.mainImage.image drawInRect:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height) blendMode:kCGBlendModeNormal alpha:self.opacity];
self.mainImage.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}
You probably have a mismatch between the size of the 2 views you're using, so as you move between the 2 different coordinate spaces you get a scaling effect.
When I try to draw in my iPad "draw application" it uses HUGE amounts of memory and eventually crashes (or at least just draw lines VERY laggy). I have been looking a lot for examples on "Drawing apps". I only found 2 that worked, but both lagged A LOT when trying to draw on them. If I use the iPhone/iPad simulator is draws smoothly, but on my iPad 2G it lags a lot :(
The real purpose of my application is a "Signature app", if it will help anybody ;)
If you got any experience with drawing apps (or can see where my code is wrong), please leave an answer. It will be appreciated!!
My code is listed below (also a screenshot):
ViewController.m:
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
drawImage = [[UIImageView alloc] initWithImage:nil];
drawImage.frame = self.view.frame;
[self.view addSubview:drawImage];
self.view.backgroundColor = [UIColor lightGrayColor];
mouseMoved = 0;
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
mouseSwiped = NO;
UITouch *touch = [touches anyObject];
lastPoint = [touch locationInView:self.view];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
mouseSwiped = YES;
UITouch *touch = [touches anyObject];
CGPoint currentPoint = [touch locationInView:self.view];
UIGraphicsBeginImageContext(self.view.frame.size);
[drawImage.image drawInRect:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
CGContextSetLineCap(UIGraphicsGetCurrentContext(), kCGLineCapRound);
CGContextSetLineWidth(UIGraphicsGetCurrentContext(), 3.0);
CGContextSetRGBStrokeColor(UIGraphicsGetCurrentContext(), 255.0, 255.0, 255.0, 1.0);
CGContextBeginPath(UIGraphicsGetCurrentContext());
CGContextMoveToPoint(UIGraphicsGetCurrentContext(), lastPoint.x, lastPoint.y);
CGContextAddLineToPoint(UIGraphicsGetCurrentContext(), currentPoint.x, currentPoint.y);
CGContextStrokePath(UIGraphicsGetCurrentContext());
drawImage.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
lastPoint = currentPoint;
mouseMoved++;
if (mouseMoved == 10) {
mouseMoved = 0;
}
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
if(!mouseSwiped) {
UIGraphicsBeginImageContext(self.view.frame.size);
[drawImage.image drawInRect:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
CGContextSetLineCap(UIGraphicsGetCurrentContext(), kCGLineCapRound);
CGContextSetLineWidth(UIGraphicsGetCurrentContext(), 3.0);
CGContextSetRGBStrokeColor(UIGraphicsGetCurrentContext(), 255.0, 255.0, 255.0, 1.0);
CGContextMoveToPoint(UIGraphicsGetCurrentContext(), lastPoint.x, lastPoint.y);
CGContextAddLineToPoint(UIGraphicsGetCurrentContext(), lastPoint.x, lastPoint.y);
CGContextStrokePath(UIGraphicsGetCurrentContext());
CGContextFlush(UIGraphicsGetCurrentContext());
drawImage.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
#end
ViewController.h:
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController {
CGPoint lastPoint;
UIImageView *drawImage;
BOOL mouseSwiped;
int mouseMoved;
}
#end
Most of the performance problem is probably from drawing the previous image on EVERY touch event. [drawImage.image drawInRect:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
The first thing I would try is to create a view derived from UIView and do all drawing in drawRect. That way you are not blocking for drawing on every touch event. When you respond to touch events you will need to call setNeedsDisplay on your custom view.
The second thing I would try is to use a CAShapeLayer for the layer backing on your custom view. When you do this you don't need to override drawRect. You just need to give the layer the points.
I am trying to use the touchesBegan, moved, and ended method, although when I use the code attached below, after the finger is released from the screen, when the image is written to the bottom view, it moves the drawing down 70-80px. I have no idea what could be causing this, as the views are aligned perfectly.
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
mouseSwiped = NO;
UITouch *touch = [touches anyObject];
lastPoint = [touch locationInView:self.view];
}
- (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)];
CGContextMoveToPoint(UIGraphicsGetCurrentContext(), lastPoint.x, lastPoint.y);
CGContextAddLineToPoint(UIGraphicsGetCurrentContext(), currentPoint.x, currentPoint.y);
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();
lastPoint = currentPoint;
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
if(!mouseSwiped) {
UIGraphicsBeginImageContext(self.view.frame.size);
[self.tempDrawImage.image drawInRect:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
CGContextSetLineCap(UIGraphicsGetCurrentContext(), kCGLineCapRound);
CGContextSetLineWidth(UIGraphicsGetCurrentContext(), brush);
CGContextSetRGBStrokeColor(UIGraphicsGetCurrentContext(), red, green, blue, opacity);
CGContextMoveToPoint(UIGraphicsGetCurrentContext(), lastPoint.x, lastPoint.y);
CGContextAddLineToPoint(UIGraphicsGetCurrentContext(), lastPoint.x, lastPoint.y);
CGContextStrokePath(UIGraphicsGetCurrentContext());
CGContextFlush(UIGraphicsGetCurrentContext());
self.tempDrawImage.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}
UIGraphicsBeginImageContext(self.mainImage.frame.size);
[self.mainImage.image drawInRect:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height) blendMode:kCGBlendModeNormal alpha:1.0];
[self.tempDrawImage.image drawInRect:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height) blendMode:kCGBlendModeNormal alpha:opacity];
self.mainImage.image = UIGraphicsGetImageFromCurrentImageContext();
self.tempDrawImage.image = nil;
UIGraphicsEndImageContext();
}
-(IBAction)paint:(id)sender {
}
-(IBAction)reset:(id)sender {
self.mainImage.image = nil;
}
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
red = 0.0/255.0;
green = 0.0/255.0;
blue = 0.0/255.0;
brush = 5.0;
opacity = 1.0;
[super viewDidLoad];
// Do any additional setup after loading the view.
}
Please make sure that Imageview & image on that imageview both are of same size.
ie. self.tempDrawImage and self.mainImage frame should be of size