OStack程序员社区-中国程序员成长平台

标题: ios - 在 iOS 中撤消/重做绘图 [打印本页]

作者: 菜鸟教程小白    时间: 2022-12-13 06:13
标题: ios - 在 iOS 中撤消/重做绘图

我正在开发一个绘图应用程序,我想做撤消/重做,为此我将触摸结束时的 CGPath 保存到 NSMutableArray,但我不明白如何在单击撤消按钮时呈现 CGPaths

编辑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)touchesBeganNSSet *)touches withEventUIEvent *)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)touchesMovedNSSet *)touches withEventUIEvent *)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 lineSegmentPerpendicularToLineSegment){pointsBuffer[i], pointsBuffer[i+1]} ofRelativeLength:frac1]; // ................. (5)
                        ls[2] = [self lineSegmentPerpendicularToLineSegment){pointsBuffer[i+1], pointsBuffer[i+2]} ofRelativeLength:frac2];
                        ls[3] = [self lineSegmentPerpendicularToLineSegment){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)touchesEndedNSSet *)touches withEventUIEvent *)event
{       
     [parentUndoArray addObject:m_undoArray];

}

下面是我的drawRect方法

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

  - (void)drawRectCGRect)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.第一屏不抬手指,一气呵成的画线截图

enter image description here

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

enter image description here



Best Answer-推荐答案


我已经找到了解决方案,我们需要创建一个 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 - 在 iOS 中撤消/重做绘图,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21438586/






欢迎光临 OStack程序员社区-中国程序员成长平台 (https://ostack.cn/) Powered by Discuz! X3.4