2014-01-29 3 views
2

Я работаю над графическим приложением, я хочу сделать Undo/Redo, для этого я сохраняю CGPath при прикосновениях, заканчивающихся до NSMutableArray, но я не понимаю, как я должен отображать CGPaths на нажмите на кнопку ОтменитьUndo/Redo для рисования в iOS

EDIT1:

Как я использую BezierPaths, Итак, я первый решил пойти с простым подходом просто поглаживая по этому пути, без CGPath,

edit2: Как моя Undo является происходит в сегментах (i, e по частям и не весь путь удаляется), я решил создать массив массивов, поэтому я внес соответствующие изменения, и теперь я буду рисовать CGlay er, с принятием 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 ниже

EDIT: теперь у меня 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: Теперь проблема, что я столкнулся в настоящее время является то, что, даже если я рисую маленькие дорожки или большие пути, данные в массиве массива же. Но Infact, малый путь должен содержать меньший объект drawingPath и большой путь должен содержать больше drawingPath объект в undoArray, который наконец-то добавляется в массив массив под названием «ParentUndoArray

Вот скриншоты,

1. первый снимок экрана нарисованной линии на участке, не снимая палец

enter image description here

2, После выполнения операции отмены один раз, только сегмент этой линии удаляется

enter image description here

ответ

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.

+0

Как реализовать функцию отмены в этих ссылках ответ ..http: // stackoverflow .com/questions/23997525/how-to-draw-calayer-with-line-path-like-simulate-eraser-effect, пожалуйста, помогите мне .. –

1

Если ваш метод DrawRect может рисовать все CGPaths в массиве, все, что вам нужно сделать, это вызвать рисунок снова вы Undo метод, с помощью вызова setNeedsDisplay после удаления последнего добавленного CGPath

+0

Здравствуйте, Niraj, если вы внимательно посмотрите на мой код, вы заметите, что я сделал переменную ширину в touchhesMoved, и вот только я просто вынимаю CgPath из UIBezeirPath и просто рисую его. – Ranjit

+0

Возможно, я плохо разбирался в этой проблеме, но, похоже, вам нужен способ перерисовать пути в вашем NSMutableArray в любой момент времени. Если это так, вы, вероятно, должны перенести код чертежа с touchsMoved на отдельный метод. – Niraj

1

Я думаю, вы создаете больше объектов пути, чем планируете. Я предлагаю пойти туда, где вы Alloc в bezierPath в касаний переехал и заменить

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

С

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

Я пробовал, что происходит, если я рисую две строки, а затем, если я нажимаю кнопку отмены, тогда вторая строка не будет идти, у меня есть два раза отменить три раза, затем вторая линия исчезает, аналогично, для первой строки – Ranjit

+0

Где код отмены, затем спариваться? Не беспокойтесь, мы сможем это сделать :) – Jef

+0

Я отправлю его, дайте мне две минуты – Ranjit

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

Надеется, что это поможет.