2014-11-13 6 views
0

Я разрабатываю приложение для раскраски, однако мне не удается реализовать кнопку UNDO. Я не уверен в этом, я пытался реализовать NSUndoManager, но я не мог заставить его работать эффективно. Возможно, мой подход неверен. Im очень оценил бы ответ, который использует код, основанный на моем примере.Как отменить линейный штрих ios

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

Были заданы одинаковые вопросы, но ответа не было дано однозначно, а некоторые остались без ответа.

* EDIT 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 
+1

Я предлагаю пройти этот https://github.com/acerbetti/ACEDrawingView и, пожалуйста, напишите, если у вас есть какие-либо вопросы. Это также приводит к отмене и повторению. – Vig

+1

@Vig очень хороший код, однако, gabblers использовали мой собственный код в своем проекте. – DevC

ответ

4

UIViewController «s суперкласс UIResponderNSUndoManager имеет свойство, которое может быть использовано здесь. Вышеприведенный код выполняет настройку действия изображения для 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 project с undo и redo возможностями.

+0

Где я вставляю код? Положив его на нижние результаты, линия сразу же стирается. См. Приведенное выше изменение. – DevC

+0

'UIImage * currentImage = UIGraphicsGetImageFromCurrentImageContext();' Эта строка должна быть помещена перед 'UIGraphicsEndImageContext()'. – gabbler

+0

Удалить эту строку: '@property (nonatomic, readonly) NSUndoManager * undoManager',' UIViewController' имеет встроенный объект 'NSUndoManager'. – gabbler