Undo with multitouch drawing in iOS
Asked Answered
S

2

2

I am working with multitouch while writing, So basically what I am doing is, I am writing with hand support, because typically, its how user rights, I followed this link How to ignore certain UITouch Points in multitouch sequence

Everything is working fine, but their is some problem with undo when I write with my hand touching the screen, otherwise it works fine.

Below is my code

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    UITouch* topmostTouch = self.trackingTouch;
    for (UITouch *touch in touches)
    {
        ctr = 0;

        touchStartPoint1 = [touch locationInView:self];


        [m_undoArray removeAllObjects];
        [m_redoArray removeAllObjects];
        [m_parentRedoArray removeAllObjects];


        if(!topmostTouch || [topmostTouch locationInView:self].y > touchStartPoint1.y)
        {
            topmostTouch = touch;
            pts[0] = touchStartPoint1;
        }
    }


    if (self.trackingTouch != nil && self.trackingTouch != topmostTouch)  //              ![touches containsObject:self.trackingTouch])
    {
        [self discardDrawing];

    }

    self.trackingTouch = topmostTouch;
}

-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{ 
    if(self.trackingTouch== nil)
    {
        return;
    }

    CGPoint p = [self.trackingTouch locationInView:self];
    ctr++;
    pts[ctr] = p;

    if (ctr == 4)
    {
        pts[3] = midPoint(pts[2], pts[4]);

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

        [self.currentPath setPathColor:self.lineColor];
        self.currentPath.pathWidth = [NSString stringWithFormat:@"%f",self.lineWidth];


        [self.currentPath.path moveToPoint:pts[0]];
        [self.currentPath.path addCurveToPoint:pts[3] controlPoint1:pts[1] controlPoint2:pts[2]];

        CGPathRef cgPath = self.currentPath.path.CGPath;
        mutablePath = CGPathCreateMutableCopy(cgPath);

        [m_undoArray addObject:self.currentPath];
        [self setNeedsDisplay];


        pts[0] = pts[3];
        pts[1] = pts[4];
        ctr = 1;
    }
}

-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{    
    for (UITouch *touch in touches)
    {
        if(touch == self.trackingTouch)
        {
             [m_parentUndoArray addObject:[NSArray arrayWithArray:m_undoArray]];                
        }          
   }
}


-(void)undoButtonClicked
{     
    NSMutableArray *undoArray = [m_parentUndoArray lastObject];

    NSLog(@"%@",undoArray);

    [m_parentUndoArray removeLastObject];
    [m_parentRedoArray addObject:undoArray];
     m_drawStep = UNDO;  

    [self setNeedsDisplay];    

}


- (void)drawRect
{
   I have different cases here, I am showing Of Undo

   for(int i = 0; i<[m_parentUndoArray count];i++)
   {
       NSMutableArray *undoArray = [m_parentUndoArray objectAtIndex:i];
       NSLog(@"%@",undoArray);

      for(int i =0; i<[undoArray count];i++)
      {
         DrawingPath *drawPath = [undoArray objectAtIndex:i];
         GPathRef path = drawPath.path.CGPath;
         mutablePath = CGPathCreateMutableCopy(path);

         //Draw into CgLayer            
     }
   }
}

Here is the image to understand my problem better, I first wrote this

enter image description here

After clicking on undo Once

After clicking on Undo once,you can see above that, some other part has been undone, instead of the last part. So I need you help in this regard.

Spirituality answered 21/2, 2014 at 12:54 Comment(8)
Do you draw in the same way before and after the Undo (Do you always use m_parentUndoArray to draw ?)?Waterfront
In touches moved as you can see, I am converting the path into CGMutablePath and the drawing..But for undoing I use ParentUndoArray.. @NicolasBonnetSpirituality
Did you test to redraw without removing the last object ? (To test the draw method with m_parentUndoArray ) @SpiritualityWaterfront
@NicolasBonnet, I dont remove last object, then what effect it will have nothing, right?Spirituality
Should be... just tell me what's happened .Waterfront
@NicolasBonnet, you are right, it is removing some random part, if I dont remove lastObjectSpirituality
Ok the work on your redraw method =) I suggest you that : for (NSArray* arrayOfPath in m_parentUndoArray) { ... } instead of for(int i = 0; i<[m_parentUndoArray count];i++) { ... }Waterfront
let us continue this discussion in chatSpirituality
C
1

m_redoArray appears to be the big daddy, the one you draw from. I don't understand why you empty this out out in 'touchesBegan...', surely one of these arrays must carry through touchesBegan unaltered, or you'll be dropping stuff from the start of your drawing all the way through, no?

It appears to me that this is how you dropped the 'Hell' in your example here..

Classy answered 21/2, 2014 at 20:18 Comment(9)
Hello @Jef, I am not using m_redoArray anywhere. I am only using m_undoArray and adding it to m_parentUndoArraySpirituality
I mean I have written it in the code shown above , but I am not using it anywhereSpirituality
Ah, well I think one of the arrays you empty out in touchesBegan... Should not be emptied at that point, need to see the rest of drawRect: .... Also don't forget to CGPathRelease(..) for every CGPathCreate or copy, there is no ARC on those, that's core foundation stuffClassy
I got it, I tried removing objects from m_undoArray in touches end, instead of touchesBegan, and its working fine..... Now I have a differnt problem, if I am writing with hand touch(mulitouch), my curves are not very smooth, but if I write with single touch, it works fine, any suggestion on this . @ClassySpirituality
No sorry. I think you're crazy to try, who writes with two Pencils at the same time? Good luck to you, but I personally see little merit in itClassy
you are misunderstood me, I am not talking about twompencils, I am talking about writing with my hand touching the screenSpirituality
i understand, one does not use a pencil or stylus or anything on touch screen. But you are talking about writing with multiple touches at once. hence, two (or more) pencils.Classy
let us continue this discussion in chatSpirituality
Hello @Jef, I created a different question for it, can you please check it #21952774Spirituality
S
0

Well in my case i have used bezierPath to draw on touch and have successfully implemented undo functionality. Here is the code :

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

Hope it will help you.

Scamander answered 21/2, 2014 at 14:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.