使用 Cglayer 绘图进行撤消和重做 [英] Doing Undo and Redo with Cglayer Drawing

查看:18
本文介绍了使用 Cglayer 绘图进行撤消和重做的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用绘图应用程序,我正在使用 CGlayers 进行绘图.在触摸结束时,我从图层中取出图像并将其存储在一个数组中,我用它来撤消操作.

I am working with a drawing app, I am using CGlayers for drawing. On touches ended, I get image out of the layer and store it in a Array, which I use to undo operation.

我的触摸结束功能

-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{    
    NSLog(@"Touches ended");

    UIGraphicsBeginImageContextWithOptions(self.bounds.size, NO, 0.0);
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextDrawLayerInRect(context, self.bounds, self.drawingLayer);
    m_curImage = UIGraphicsGetImageFromCurrentImageContext();  
    UIGraphicsEndImageContext(); 

    [m_undoArray addObject: m_curImage];
}

我的绘图视图根据用户需求动态扩展,因此假设用户可以绘制一条 drawView 大小为 200*200 的线,然后将其扩展为 200*300 并再绘制一条线,然后将其扩展为 200*300 并绘制多一条线.

My Drawing View expands dynamically on user demand, So suppose user can draw say, a line with drawView size 200*200, then expand it to 200*300 and draw one more line, then expand it to 200*300 and draw one more line.

这是应用程序的图像

所以现在我在 UndoArray 中有 3 个不同大小的图像.

So now I have 3 images with different sizes in UndoArray.

每当我增加/减少画布大小时.我已经写了这段代码

Whenever I increase/decrese the canvas size. I have written this code

关于drawingView的增减,我在写这个函数

On increase and decrease of the drawingView, I am writing this function

 (void)increaseDecreaseDrawingView 
{ 
self.currentDrawingLayer = nil; 

if(self.permanentDrawingLayer) 
{ 
rectSize = self.bounds; 
NSLog(@"Size%@", NSStringFromCGRect(self.bounds)); 
CGContextRef context = UIGraphicsGetCurrentContext(); 

//self.newDrawingLayer = CGLayerCreateWithContext(context, self.bounds.size, NULL); 
CGFloat scale = self.contentScaleFactor; 
CGRect bounds = CGRectMake(0, 0, self.bounds.size.width * scale, self.bounds.size.height * scale); 
CGLayerRef layer = CGLayerCreateWithContext(context, bounds.size, NULL); 
CGContextRef layerContext = CGLayerGetContext(layer); 
CGContextScaleCTM(layerContext, scale, scale); 
self.newDrawingLayer = layer; 


CGContextDrawLayerInRect(layerContext, self.bounds, self.permanentDrawingLayer ); 

self.permanentDrawingLayer = nil; 

} 

为了撤消,我写了这段代码

And for doing undo I have written this code

- (void)Undo
{
     //Destroy the layer and create it once again with the image you get from undoArray.
     self.currentDrawingLayer = Nil;

     CGContextRef layerContext1 = CGLayerGetContext(self.permanentDrawingLayer );
     CGContextClearRect(layerContext1, self.bounds);

     CGContextRef context = UIGraphicsGetCurrentContext();

    for(int i =0; i<[m_rectArrayUndo count];i++)
    {
        CGRect rect = [[m_rectArrayUndo objectAtIndex:i]CGRectValue];
        CGLayerRef undoLayer = CGLayerCreateWithContext(context, rect.size, NULL);

        CGContextRef layerContext = CGLayerGetContext(undoLayer );
        CGContextTranslateCTM(layerContext, 0.0, rect.size.height);
        CGContextScaleCTM(layerContext, 1.0, -1.0);

        CGRect imageFrame;

        NSDictionary *lineInfo = [m_undoArray objectAtIndex:i];
        m_curImage = [lineInfo valueForKey:@"IMAGE"];
       imageFrame = CGRectMake(0 ,0,m_curImage.size.width,m_curImage.size.height);
       CGContextDrawImage(layerContext, imageFrame, m_curImage.CGImage);
       CGContextDrawLayerInRect(context, rect, undoLayer );
       CGContextDrawLayerInRect(layerContext1, rect, undoLayer);
    }          
}

在我的drawRect函数中,我写了这段代码

In my drawRect function, I have written this code

- (void)drawRect:(CGRect)rect
{    

            CGContextRef context = UIGraphicsGetCurrentContext();//Get a reference to current context(The context to draw)

            if(self.currentDrawingLayer == nil)
            {                
                CGLayerRef layer = CGLayerCreateWithContext(context, bounds.size, NULL);                             
                self.currentDrawingLayer = layer;
            }



            if(self.permanentDrawingLayer == nil)
            {
                CGLayerRef layer = CGLayerCreateWithContext(context, bounds.size, NULL);
                self.permanentDrawingLayer = layer;
            }


            if(self.newDrawingLayer == nil)
            {
                CGLayerRef layer = CGLayerCreateWithContext(context, bounds.size, NULL);
                self.newDrawingLayer = layer;
            }

            CGPoint mid1 = midPoint(m_previousPoint1, m_previousPoint2);
            CGPoint mid2 = midPoint(m_currentPoint, m_previousPoint1);



            CGContextRef layerContext = CGLayerGetContext(self.currentDrawingLayer);

            CGContextSetLineCap(layerContext, kCGLineCapRound);
            CGContextSetBlendMode(layerContext, kCGBlendModeNormal);
            CGContextSetLineJoin(layerContext, kCGLineJoinRound);
            CGContextSetLineWidth(layerContext, self.lineWidth);
            CGContextSetStrokeColorWithColor(layerContext, self.lineColor.CGColor);
            CGContextSetShouldAntialias(layerContext, YES);
            CGContextSetAllowsAntialiasing(layerContext, YES);
            CGContextSetAlpha(layerContext, self.lineAlpha);
            CGContextSetFlatness(layerContext, 1.0f);
            CGContextBeginPath(layerContext);
            CGContextMoveToPoint(layerContext, mid1.x, mid1.y);//Position the current point
            CGContextAddQuadCurveToPoint(layerContext, m_previousPoint1.x, m_previousPoint1.y, mid2.x, mid2.y);
            CGContextStrokePath(layerContext);//paints(fills) the line along the current path.

            CGContextDrawLayerInRect(context, self.bounds, self.newDrawingLayer);

            CGContextDrawLayerInRect(context,  self.bounds, self.permanentDrawingLayer);
            CGContextDrawLayerInRect(context, self.bounds, self.currentDrawingLayer);

            [super drawRect:rect];
}

我没有什么疑问

  1. 这是正确的做法吗?还是他们有更好的方法.

  1. Is this the correct way to do? Or is their any better approach.

这里发生的情况是,我的撤消数组中的图像不尊重矩形,而是在新层上的任何随机位置绘制.

Here What happens is that, my images from undo array are not respecting the rects and are drawn at any random position on the new Layer.

所以我想知道我们如何正确地绘制它们,以便在特定位置的 CGlayers 上正确地绘制图像.

So I want to know how we can draw them properly so that images are drawn properly on CGlayers at specific position.

推荐答案

首先,既然你是在处理图层,我建议你放弃 drawRect: 而只使用 CALayer 转换.

First of all, since you are working with layers, I suggest give up on drawRect: and just work with CALayer transforms.

其次,在我看来,实现撤消重做操作的最佳方式始终是基于命令的.作为一个非常简单的示例,您可以为每个命令创建单独的方法:

Second, in my opinion, the best way to implement undo-redo operations will always be command-based. As a very simple example, you can make separate methods for each command:

- (void)scaleLayerBy:(CGFloat)scale;
- (void)moveLayerByX:(CGFloat)x Y:(CGFloat)y;
// etc

然后每次用户进行操作时,您将操作 id 和参数添加到 NSMutableArray:

And then each time the user makes an action, you add to an NSMutableArray the action id and the parameters:

[self.actionHistory addObject:@{ @"action": @"move", @"args": @[@10.0f, @20.0f] }];

相反,如果用户调用 undo,则删除该数组中的最后一个对象.

Conversely, if the user invokes undo, remove the last object in that array.

然后当您需要重新加载显示时,只需重新评估数组中的所有命令即可.

Then when you need to reload the display, just reevaluate all the commands in the array.

[self resetLayers]; // reset CALayers to their initial state
for (NSDictionary *command in self.actionHistory) {
    NSArray *arguments = command[@"args"];
    if ([command[@"action"] isEqualToString:@"move"]) {
        [self moveLayerByX:[arguments[0] floatValue] Y:[arguments[1] floatValue]];
    }
    // else if other commands
}

这篇关于使用 Cglayer 绘图进行撤消和重做的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆