【问题标题】:How to undo a line stroke ios如何撤消线条笔划ios
【发布时间】:2023-04-01 20:50:02
【问题描述】:

我正在开发一个着色应用程序,但是我无法实现 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();
}

有人问过类似的问题,但没有给出明确的答案,还有一些没有得到回答。

* 根据 gabbler 答案编辑 1 *

#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

【问题讨论】:

标签: ios undo uigraphicscontext


【解决方案1】:

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

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];
}

编辑

上述方法会导致内存问题,不要将图像保存在undoManager,而是保存线路径点,这里有一个sample projectundoredo功能。

【讨论】:

  • 代码应该放在touches ended 的什么地方?将其放在底部会导致该行立即被删除。请参阅上面的编辑。
  • UIImage *currentImage = UIGraphicsGetImageFromCurrentImageContext(); 此行应放在UIGraphicsEndImageContext() 之前。
  • 删除这一行:@property (nonatomic,readonly) NSUndoManager * undoManagerUIViewController 有一个内置的NSUndoManager 对象。
  • 我注意到了一个问题。如果您使用多种颜色,则颜色不会持续存在。例如,如果我先画一条黑线,然后画一条红线,然后撤消红线,黑线现在是红色的?
  • 当前的实现是当你撤消时,它会用相同的颜色重绘所有现有的线,你可以在其中添加一个带有颜色信息的数组,例如,array[0] = black, array[1 ] = red,表示第0行颜色为黑色,第1行颜色为红色,重绘时从数组中获取对应颜色。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-02
  • 1970-01-01
  • 2020-10-14
相关资源
最近更新 更多