Undo/Redo for drawing in iOS(在 iOS 中撤消/重做绘图)
问题描述
我正在开发一个绘图应用程序,我想做撤消/重做,为此我将触摸结束时的 CGPath 保存到 NSMutableArray,但我不明白如何在单击撤消按钮时呈现 CGPaths
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
由于我使用的是 BezierPaths,所以,我首先决定采用一种简单的方法,即在不使用 CGPath 的情况下抚摸这条路径,
As I am using BezierPaths, So, I first decided to go with a simple approach of just stroking this path, without CGPath,
由于我的撤消是在分段中进行的(即,部分而不是整个路径被删除),我决定创建一个数组数组,所以我做了相应的更改,现在我将在 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];
}
现在我面临的问题是,即使我绘制小路径或大路径,数组数组中的数据都是相同的.但事实上,小路径应该包含较少的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
这是屏幕截图,
1.第一屏不抬手指,一气呵成的画线截图
1.first screen Shot of drawn line at a stretch without lifting the finger
2、执行一次undo操作后,只删除该行的一部分
2, After doing undo operation once, only segment of that line is removed
推荐答案
我已经找到了解决方案,我们需要创建一个 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 中撤消/重做绘图的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!
本文标题为:在 iOS 中撤消/重做绘图


- Android viewpager检测滑动超出范围 2022-01-01
- android 4中的android RadioButton问题 2022-01-01
- 用 Swift 实现 UITextFieldDelegate 2022-01-01
- Android - 我如何找出用户有多少未读电子邮件? 2022-01-01
- 在测试浓缩咖啡时,Android设备不会在屏幕上启动活动 2022-01-01
- 如何检查发送到 Android 应用程序的 Firebase 消息的传递状态? 2022-01-01
- 想使用ViewPager,无法识别android.support.*? 2022-01-01
- MalformedJsonException:在第1行第1列路径中使用JsonReader.setLenient(True)接受格式错误的JSON 2022-01-01
- 使用自定义动画时在 iOS9 上忽略 edgesForExtendedLayout 2022-01-01
- Android - 拆分 Drawable 2022-01-01