Wednesday, May 18, 2011

UIBezierPath tutorial for iPhone SDK 4.0 - Part 1


I was going through how to use UIBezierPath and what are its possible uses.. and I must say it is one hell of a convenient tool ! Ofcourse you can draw normal drawing with it as shown below and use your app as sketching pad in addition to that it adds so much convenience with the undo and redo action(will write that tutorial too next) that I fell in love with the use of UIBezierPath. You can have simple drawing as well as pattern based brush tip.   It is sooo easy to use this as pencil too that I don't even have words !





You can init the UIBezierPath as following


        UIBezierPath *myPath=[[UIBezierPath alloc]init];
        myPath.lineWidth=10;
        brushPattern=[UIColor redColor]; //This is the color of my stroke
        




Then you have Touch methods which handle and track the coordinates of your touch. When your touch begins on the screen, you ask UIBezierPath to move to  that touch  point

    UITouch *mytouch=[[touches allObjectsobjectAtInd
    [myPath moveToPoint:[mytouch locationInView:self]];



As you move your finger around, you keep adding those points in your BezierPath in TouchMoved method by following

    UITouch *mytouch=[[touches allObjects] objectAtIndex:0];
    [myPath addLineToPoint:[mytouch locationInView:self]];

As we need constant refreshing of the screen, so that as soon as we draw it appears on the screen, we refresh the UIView subclass by calling following method in TouchMethod so that as soon as there any change in the BezierPath, it is reflected on the screen.

[self setNeedsDisplay];



Talking about drawRect Method which does all the drawing for you, you need to set the color of your stroke(stroke color means the color with which painting will be done on screen.) on screen and also  the blend mode. You can try different blend mode and see the result.
- (void)drawRect:(CGRect)rect
{
    [brushPattern setStroke];
    [myPath strokeWithBlendMode:kCGBlendModeNormal alpha:1.0];

}








Pattern brush.. doesn't exactly work the way it is in Photoshop , because once you fill the whole screen with your drawing, the screen looks completely like a screen full of tiles of your pattern which is not what I would want but ... thats what we have so far in terms of pattern brush. I am trying to implement the brush with opengl lets see if I succeed then I will post it here !


myPath.lineWidth=10;//This can help you in setting width of your stroke/brush 
brushPattern=[[UIColor alloc]initWithPatternImage:[UIImage imageNamed:@"pattern.jpg"]]; //You can set pattern in your color of your brush so that the strokes will appear with pattern of your image.


















































You can download the code HERE

Update:
Thanks to Sonia for pointing it out , wanted to know about smoothing of edges of bezier path, so that it doesn't look distorted . You can change the  capStyle of line for that. I have changed it and updated the code at above link.





















Well.. Finally implemented and uploaded code implementing Undo-Redo.
POSTED HERE




19 comments:

  1. Thanks for the tutorial. But, how can we implement the undo? Any update for this?

    ReplyDelete
  2. I have posted an article about how to implement undo-redo in BezierPath
    http://soulwithmobiletechnology.blogspot.com/2011/06/redo-undo-in-paint-feature.html

    ReplyDelete
    Replies
    1. Reetu, I am having a devil of a time merging the undo/redo code with the line drawing code. The difficulty is that I have segmented controls for brushes, colors and background pictures. I have posted a question on StackOverflow here: http://stackoverflow.com/questions/10142404/xcode-undo-a-bezier-path, but no answers so far. Would you be willing to have a look at the offending methods? I would really appreciate that.

      David

      Delete
  3. great tutorial.

    is there a straightforward way to add an image to the MyLineDrawingView background to draw "over". ?

    ReplyDelete
  4. This comment has been removed by the author.

    ReplyDelete
  5. Hi reetu, hope u are fine...great tutorial once again.I only want to draw straight lines, start it when the user double tap on the screen & end it when the user again double tap on the screen.How can do it with UIBezierPath?

    ReplyDelete
  6. Nice drawing app!
    And

    REETU RAJ nice redoo undo

    ReplyDelete
  7. I have been searching for this technique for some time. It sure is better than the Apple GLPaint sample. Thank you so much for figuring this out and making it available. You are very generous.

    David

    ReplyDelete
  8. One thing...Reetu, how would I save the screen - either as a file or a screenshot?

    Thanks!!

    ReplyDelete
  9. I will post the little code snippet for that.

    ReplyDelete
    Replies
    1. Thanks. That would be wonderful...

      Delete
    2. Hi Reeta, I got this code to work - it saves the image to the photo album. Maybe other can use it - probably, you can improve it.. David

      ps. The flash screen method is cool. It signals that the save worked...


      - (IBAction)saveButtonTapped:(id)sender
      {
      NSLog(@"%s", __FUNCTION__);

      [self captureToPhotoAlbum];
      [self flashScreen];

      }


      -(void)captureToPhotoAlbum {
      NSLog(@"%s", __FUNCTION__);
      // UIImageWriteToSavedPhotosAlbum(drawImage.image, self, nil, nil);
      //return;

      // Get image to save
      CGImageRef screen = UIGetScreenImage();
      UIImage *img = [UIImage imageWithCGImage:screen];

      // Image cropping
      CGImageRef imageRef = CGImageCreateWithImageInRect([img CGImage], CGRectMake(0.0f, 70.0f, 768.0f, 768.0f));
      UIImage *img2Save = [UIImage imageWithCGImage:imageRef];
      CGImageRelease(imageRef);

      // Request to save the image to camera roll
      UIImageWriteToSavedPhotosAlbum(img2Save, self, nil, nil);

      }

      -(void) flashScreen {
      NSLog(@"%s", __FUNCTION__);
      UIWindow* wnd = [UIApplication sharedApplication].keyWindow;
      UIView* v = [[UIView alloc] initWithFrame: CGRectMake(0, 0, wnd.frame.size.width, wnd.frame.size.height)];
      [wnd addSubview: v];
      v.backgroundColor = [UIColor whiteColor];
      [UIView beginAnimations: nil context: nil];
      [UIView setAnimationDuration: 1.0];
      v.alpha = 0.0f;
      [UIView commitAnimations];
      }

      Delete
  10. Thank you mam for your code.
    Mam I am developing a book app in ipad where user can write notes. For that i need a pen like structure through which user can make note and save it and will see it again when he came to same page or where ever i list him notes. So how to implement them and save them so that user can see his notes same as he wrote. I never work on drawing before in iphone/ipad. I will be obliged for any guidance and help.

    ReplyDelete
  11. Hello mam,
    Mam I want to create a notebook like app like bamboo app, neu notepad etc
    I have started reading coregraphics for that
    But not getting right approach what should be read and from where.
    because in my previous app I just started working on it without knowing cons and prons of the library i used and hence results into disaster. This time i dont want to repeat that. An app in which one can write, draw , erase, color change etc. (the functionality required by a notebook.)
    Can you suggest me some tutorial or site from where I can read and able to develop such App by my own

    Thank you in advance

    ReplyDelete
  12. @Sonia, You can easily use UIBezierPath for all the needs you mentioned above - write/draw/erase/color change of pen.
    This is just an introductory tutorial, you can read more about it on Apple's website. to learn more deeply about it.
    I think you have just started in this application.
    I wish you best of luck, I am sure this reply is good enough to guide you in the right direction.

    ReplyDelete
  13. I just wanted to confirm is bezier curve is enough for writing and sketching so that i can proceed in that. From your reply i got it. As with the code you provided not able to draw smoothly and not able to put simple dots so i was little bit confused but now I started working on it.
    Thank You mam for your reply.

    ReplyDelete
  14. Thanks for your nice tutorial.
    I integrate it in my app but in my app there is extra feature i.e. to erase the path which we draw using an eraser.
    User has an option to change the size of eraser.
    I googled it & fount the solution that use pattern image similar to the background but I dont want this.
    If you have any option the please help me.
    Thanks.

    ReplyDelete
  15. @Sonia, you can change the capStyle, I have changed it and updated the code on the link to download.

    @Giri, you can use the Basic draw line method with CGContext and changing to BlendMode of CGContext to Clear.

    ReplyDelete
  16. I tried it using following code -
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    CGContextSetBlendMode(ctx, kCGBlendModeClear);
    CGContextBeginTransparencyLayer(ctx, NULL);
    {
    CGContextSetGrayFillColor(ctx, 0.0, 1.0); // solid black

    CGContextAddPath(ctx, pathToErase);
    CGContextFillPath(ctx);
    // the above two lines could instead be CGContextDrawImage()
    // or whatever else you're using to clear
    }
    CGContextEndTransparencyLayer(ctx);

    it draws he line but with black color.
    I googled lots with no luck.
    If you have possible then please share some amount of code with me.

    ReplyDelete