撤消/重做在iOS中进行绘图

我正在做一个绘图应用程序,我想要做撤销/重做,为此我保存CGPath的触摸结束了NSMutableArray,但我不理解如何渲染CGPaths点击撤消button

EDIT1:

当我使用BezierPaths的时候,我首先决定用一个简单的方法去简单地去处理这个path,没有CGPath,

编辑2:因为我的撤消发生在段(我,部分而不是整个path被删除),我决定创build一个数组的数组,所以我做了相应的变化,现在我将在CGlayer中绘制,采取CGPath

所以这里“parentUndoArray”是数组的数组。

所以我这样做了

我有一个叫做Draw​​ingPath的类来完成绘图

//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中,我这样做

 -(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方法如下

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

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

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

这里是屏幕截图,

1.第一个画面不拉起手指,一笔一画地画出画线

在这里输入图像说明

2,撤销操作一次后,只删除该行的一段

在这里输入图像说明

我find了一个解决scheme,我们需要创build一个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]]; } 

然后在DrawRectDrawRectpath。

如果您的drawRect方法可以绘制数组中的任何CGPath,则只需在您中再次触发绘图,在删除最后一个添加的CGPath后,通过调用setNeedsDisplay来撤消方法

我认为你正在制作比你打算更多的path对象。 我build议去你所分配的bezierPath的位置移动并replace

 self.currentPath = [[DrawingPath alloc] init]; 

 if(!self.currentPath){ self.currentPath = [[DrawingPath alloc] init]; } 

那么你应该这样做,因为它很容易

 - (id)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { // Initialization code self.backgroundColor = [UIColor clearColor]; myPath = [[UIBezierPath alloc] init]; myPath.lineCapStyle = kCGLineCapRound; myPath.miterLimit = 0; bSize=5; myPath.lineWidth = bSize; brushPattern = [UIColor whiteColor]; // Arrays for saving undo-redo steps in arrays pathArray = [[NSMutableArray alloc] init]; bufferArray = [[NSMutableArray alloc] init]; } return self; } // Only override drawRect: if you perform custom drawing. // An empty implementation adversely affects performance during animation. - (void)drawRect:(CGRect)rect { [brushPattern setStroke]; for (id path in pathArray){ if ([path isKindOfClass:[UIBezierPath class]]) { UIBezierPath *_path=(UIBezierPath *)path; [_path strokeWithBlendMode:kCGBlendModeNormal alpha:1.0]; } } } #pragma mark - Touch Methods -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { UITouch *mytouch = [[touches allObjects] objectAtIndex:0]; myPath = [[UIBezierPath alloc] init]; myPath.lineWidth = bSize; [myPath moveToPoint:[mytouch locationInView:self]]; [pathArray addObject:myPath]; } -(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { [myPath addLineToPoint:[[touches anyObject] locationInView:self]]; [self setNeedsDisplay]; } #pragma mark - Undo Method -(void)undoButtonClicked { if([pathArray count]>0) { if ([[pathArray lastObject] isKindOfClass:[SPUserResizableView class]]) { [[pathArray lastObject] removeFromSuperview]; } UIBezierPath *_path = [pathArray lastObject]; [bufferArray addObject:_path]; [pathArray removeLastObject]; [self setNeedsDisplay]; } } -(void)setBrushSize: (CGFloat)brushSize { bSize=brushSize; } -(void)redoButtonClicked { if([bufferArray count]>0){ UIBezierPath *_path = [bufferArray lastObject]; [pathArray addObject:_path]; [bufferArray removeLastObject]; [self setNeedsDisplay]; } } -(void)undoAllButtonClicked { [pathArray removeAllObjects]; [self setNeedsDisplay]; } 

希望它会有所帮助。