如何撤消行笔画ios

时间:2014-11-13 14:33:59

标签: ios undo uigraphicscontext

我正在开发一个着色应用程序,但我无法设法实现UNDO按钮。我不确定这种方法,我尝试过实施NSUndoManager,但我无法让它有效地工作。我的方法可能不正确。我会非常感谢使用代码的答案,基于我的例子。

- (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();
}

也有类似的问题,但没有给出明确的答案,还有一些没有答案。

*编辑1基于gabbler答案*

#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

1 个答案:

答案 0 :(得分:4)

UIViewController的超类UIResponder具有NSUndoManager属性,可在此处使用。上面的代码为imageView执行设置图像操作,要撤消操作,您必须保留对imageView设置的上一个图像的引用。您可以在viewDidLoad中设置名为previousImage的实例变量,设置

previousImage = self.tempImage.image;

这是为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;
}

touchesEnded

UIImage *currentImage = UIGraphicsGetImageFromCurrentImageContext();
[self setImage:currentImage fromImage:previousImage];

单击按钮进行撤消时。

- (IBAction)btnClicked:(id)sender {
    [self.undoManager undo];
}

修改

上述方法会导致内存问题,请勿在{{1​​}}中保存图片,而是保存线路径点,这里是sample project undoManagerundo能力。