撤消/重做以在iOS中绘图 [英] Undo/Redo for drawing in iOS

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

问题描述

我正在绘制一个绘图应用程序,我想做Undo / Redo,为此我在结束NSMutableArray的触摸上保存CGPath,但我不明白我应该如何在点击撤消按钮时渲染CGPath

I am working on a drawing app, I want to do Undo/Redo, for this I am saving CGPath on touches ended to NSMutableArray, But I am not understanding how I should render the CGPaths on click of Undo button

EDIT1:

当我使用BezierPaths时,我首先决定采用一种简单的方法来抚摸这个路径,没有CGPath,

As I am using BezierPaths, So, I first decided to go with a simple approach of just stroking this path, without CGPath,

EDIT2:由于我的Undo发生在片段中(i,e部分而不是整个路径被删除),我决定创建一个数组数组,所以我做了相应的更改,现在我将在CGlayer中绘图,并采用CGPath

As my Undo is happening in segments(i,e in parts and not whole path is deleted), I decided to create an array of array, So I have made changes accordingly and now I will be drawing in CGlayer, with taking CGPath

所以这里parentUndoArray是数组数组。

So here "parentUndoArray" is array of arrays.

所以我这样做了

我有一个名为DrawingPath的类,它将进行绘图

I have a class called DrawingPath which will do the drawing

//DrawingPath.h

@interface DrawingPath : NSObject

@property (strong, nonatomic) NSString    *pathWidth;
@property (strong,nonatomic) UIColor      *pathColor;
@property (strong,nonatomic) UIBezierPath *path;

- (void)draw;

@end

//DrawingPath.m

#import "DrawingPath.h"

@implementation DrawingPath


@synthesize pathWidth = _pathWidth;
@synthesize pathColor = _pathColor;
@synthesize path = _path;


- (id)init {

    if (!(self = [super init] ))
        return nil;



    _path = [[UIBezierPath alloc] init];
    _path.lineCapStyle=kCGLineCapRound;
    _path.lineJoinStyle=kCGLineJoinRound;

    [_path setLineWidth:2.0f];

    return self;
}

- (void)draw
{

    [self.pathColor setStroke];
    [self.path stroke];

}

所以现在在我的DrawingView中,我这样做

So now in my DrawingView, I do it this way

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
     ctr = 0;
    bufIdx = 0;
    UITouch *touch = [touches anyObject];
    pts[0] = [touch locationInView:self];
    isFirstTouchPoint = YES;

    [m_undoArray removeAllObjects];//On every touches began clear undoArray

}

-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
            UITouch *touch = [touches anyObject];

            CGPoint p = [touch locationInView:self];
            ctr++;
            pts[ctr] = p;


            if (ctr == 4)
            {
                pts[3] = midPoint(pts[2], pts[4]);


                for ( int i = 0; i < 4; i++)
                {
                    pointsBuffer[bufIdx + i] = pts[i];
                }

                bufIdx += 4;

                dispatch_async(drawingQueue, ^{


                    self.currentPath = [[DrawingPath alloc] init];
                    [self.currentPath setPathColor:self.lineColor];

                    if (bufIdx == 0) return;

                    LineSegment ls[4];
                    for ( int i = 0; i < bufIdx; i += 4)
                    {
                        if (isFirstTouchPoint) // ................. (3)
                        {

                            ls[0] = (LineSegment){pointsBuffer[0], pointsBuffer[0]};
                            [self.currentPath.path moveToPoint:ls[0].firstPoint];


                           // [offsetPath addLineToPoint:ls[0].firstPoint];
                            isFirstTouchPoint = NO;

                        }
                        else
                        {
                            ls[0] = lastSegmentOfPrev;

                        }


                        float frac1 = self.lineWidth/clamp(len_sq(pointsBuffer[i], pointsBuffer[i+1]), LOWER, UPPER); // ................. (4)
                        float frac2 = self.lineWidth/clamp(len_sq(pointsBuffer[i+1], pointsBuffer[i+2]), LOWER, UPPER);
                        float frac3 = self.lineWidth/clamp(len_sq(pointsBuffer[i+2], pointsBuffer[i+3]), LOWER, UPPER);


                        ls[1] = [self lineSegmentPerpendicularTo:(LineSegment){pointsBuffer[i], pointsBuffer[i+1]} ofRelativeLength:frac1]; // ................. (5)
                        ls[2] = [self lineSegmentPerpendicularTo:(LineSegment){pointsBuffer[i+1], pointsBuffer[i+2]} ofRelativeLength:frac2];
                        ls[3] = [self lineSegmentPerpendicularTo:(LineSegment){pointsBuffer[i+2], pointsBuffer[i+3]} ofRelativeLength:frac3];


                        [self.currentPath.path  moveToPoint:ls[0].firstPoint]; // ................. (6)
                        [self.currentPath.path  addCurveToPoint:ls[3].firstPoint controlPoint1:ls[1].firstPoint controlPoint2:ls[2].firstPoint];
                        [self.currentPath.path  addLineToPoint:ls[3].secondPoint];
                        [self.currentPath.path  addCurveToPoint:ls[0].secondPoint controlPoint1:ls[2].secondPoint controlPoint2:ls[1].secondPoint];
                        [self.currentPath.path  closePath];

                        lastSegmentOfPrev = ls[3]; // ................. (7)
                    }     


                    [m_undoArray addObject:self.currentPath];

                     EDIT:2 
                    CGPathRef cgPath = self.currentPath.path.CGPath;
                    mutablePath = CGPathCreateMutableCopy(cgPath);


                    dispatch_async(dispatch_get_main_queue(), ^{
                    bufIdx = 0;
                    [self setNeedsDisplay];

                        });
                    });


                pts[0] = pts[3];
                pts[1] = pts[4]; 
                ctr = 1;
            }
        }
    }
}

-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{       
     [parentUndoArray addObject:m_undoArray];

}

我的drawRect方法低于

My drawRect method is below

编辑:现在我的DrawRect有两种情况

Now my DrawRect has two cases

  - (void)drawRect:(CGRect)rect
{    
    switch (m_drawStep)
   {
       case DRAW:
       {

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



           CGContextRef layerContext = CGLayerGetContext(self.currentDrawingLayer);
           CGContextBeginPath(layerContext);
           CGContextAddPath(layerContext, mutablePath);
           CGContextSetStrokeColorWithColor(layerContext, self.lineColor.CGColor);
           CGContextSetFillColorWithColor(layerContext, self.lineColor.CGColor);
           CGContextSetBlendMode(layerContext,kCGBlendModeNormal);
           CGContextDrawPath(layerContext, kCGPathFillStroke);
          // CGPathRelease(mutablePath);



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

          }
           break;




       case UNDO:
       {           
           for(int i = 0; i<[m_parentUndoArray count];i++)
           {
               NSMutableArray *undoArray = [m_parentUndoArray objectAtIndex:i];

               for(int i =0; i<[undoArray count];i++)
               {
                   DrawingPath *drawPath = [undoArray objectAtIndex:i];
                   [drawPath draw];
               }
           }


       }
           break;


       [super drawRect:rect];
}

EDIT2:现在我面临的问题是,即使我绘制小路径或大路径,数组数组中的数据是相同的。但实际上,小路径应该包含较小的drawingPath对象,而大路径应该在undoArray中包含更多的drawingPath对象,最后将其添加到名为ParentUndoArray

Now the problem what I am facing now is that, even if I draw small paths or large paths, the data in the array of array is same. But infact, small path should contain lesser drawingPath object and large path should contain more drawingPath object in undoArray, which at last is added to array of array called "ParentUndoArray

的数组数组中屏幕截图,

Here are the the screen shots,

1.第一个屏幕在不拉动手指的情况下一次性拍摄画线

1.first screen Shot of drawn line at a stretch without lifting the finger

2,撤消后操作一次,只删除该行的一部分

2, After doing undo operation once, only segment of that line is removed

推荐答案

我找到了一个解决方案,我们需要创建一个<$ c的数组数组$ c> DrawingPaths :

I have found a solution for this, we need to create a array of array of DrawingPaths:

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
   // Do the above code, then
   [m_undoArray addObject:self.currentPath];
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
   [m_parentUndoArray addObject:[NSArray arrayWithArray:m_undoArray]];
}

然后在 DrawRect

这篇关于撤消/重做以在iOS中绘图的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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