3

我正在开发一个绘图应用程序,我想做撤消/重做,为此我将 CGPath 保存到 NSMutableArray,但我不明白如何在单击撤消按钮时渲染 CGPath

编辑1:

由于我使用的是 BezierPaths,所以,我首先决定采用一种简单的方法,即在没有 CGPath 的情况下抚摸这条路径,

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

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

所以我这样做了

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

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

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

这是屏幕截图,

1.第一屏 不抬手指一气呵成的画线截图

在此处输入图像描述

2、undo操作一次后,只删除该行的一部分

在此处输入图像描述

4

4 回答 4

2

我已经找到了一个解决方案,我们需要创建一个数组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.

于 2014-02-02T04:20:21.307 回答
1

我认为您制作的路径对象超出了您的预期。我建议去你在触摸中分配 bezierPath 的地方移动并替换

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

if(!self.currentPath){
                    self.currentPath = [[DrawingPath alloc] init];
}
于 2014-01-31T12:34:29.510 回答
1

如果您的 drawRect 方法可以绘制数组中的任何 CGPath,那么您需要做的就是在删除最后添加的 CGPath 后调用 setNeedsDisplay 在 Undo 方法中再次触发绘制

于 2014-01-29T18:45:20.287 回答
0

好吧,您应该这样做,因为它很容易

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

希望它会有所帮助。

于 2014-02-21T14:17:31.877 回答