Draw UIBezierPath on UIImageView for image crop - ios

I am working on a photo editing application and need the user to be able to draw a path to crop out the photo. I have a class that works with a UIView for drawing smooth UIBezierPath lines. However I really need to apply this to a UIImageView so that the drawing can be done over the image. If I change the class to subclass UIImageView instead it no longer works. Any ideas on what I can do to fix this? Or better options for achieving the same goal? Below is my implementation:
#import "DrawView.h"
#implementation DrawView
{
UIBezierPath *path;
}
- (id)initWithCoder:(NSCoder *)aDecoder // (1)
{
if (self = [super initWithCoder:aDecoder])
{
[self setMultipleTouchEnabled:NO]; // (2)
[self setBackgroundColor:[UIColor whiteColor]];
path = [UIBezierPath bezierPath];
[path setLineWidth:2.0];
}
return self;
}
- (void)drawRect:(CGRect)rect // (5)
{
[[UIColor blackColor] setStroke];
[path stroke];
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint p = [touch locationInView:self];
[path moveToPoint:p];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint p = [touch locationInView:self];
[path addLineToPoint:p]; // (4)
[self setNeedsDisplay];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
[self touchesMoved:touches withEvent:event];
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
[self touchesEnded:touches withEvent:event];
}
#end
If I place a break point on touchesBegan or touchesMoved it does fire when expected.

First problem - userInteractionEnabled property should be set to YES for receiving touch events.
Second one - drawRect: method is not called for UIImageView subclasses:
The UIImageView class is optimized to draw its images to the display.
UIImageView will not call drawRect: a subclass. If your subclass needs
custom drawing code, it is recommended you use UIView as the base
class.
So, DrawView should be UIView subclass, that will draw UIImage in drawRect:, before bezier path drawing. Something like that (only changed parts of code):
// DrawView.h
#interface DrawView : UIView
#property (nonatomic, strong) UIImage* image;
#end
// DrawView.m
#implementation DrawView
- (void)drawRect:(CGRect)rect
{
[image drawInRect:self.bounds];
[[UIColor blackColor] setStroke];
[path stroke];
}
#end

Related

Cant draw on UIImage inside a UIView

I have a custom UIView , inside which I have to show an image as a background and draw freely on to it using touches and drags, referred to this question iOS: iOS: Drawing line is appearing behind subviews,
but doesn't seems to be working,it draws image but doesn't draw anything over it , please help
Following custom UiView draws free hand
- (id)initWithCoder:(NSCoder *)aDecoder // (1)
{
if (self = [super initWithCoder:aDecoder])
{
[self setMultipleTouchEnabled:NO]; // (2)
[self setBackgroundColor:[UIColor whiteColor]];
path = [UIBezierPath bezierPath];
[path setLineWidth:2.0];
}
return self;
}
- (void)drawRect:(CGRect)rect // (5)
{
[[UIColor blackColor] setStroke];
[path stroke];
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint p = [touch locationInView:self];
[path moveToPoint:p];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint p = [touch locationInView:self];
[path addLineToPoint:p]; // (4)
[self setNeedsDisplay];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
[self touchesMoved:touches withEvent:event];
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
[self touchesEnded:touches withEvent:event];
}
#end
This is the main UIView inside I have added UiImage and OverlayView
- (id)initWithCoder:(NSCoder *)aDecoder // (1)
{
if (self = [super initWithCoder:aDecoder])
{
CGRect rect1 = self.frame;
rect1.origin.x = 0;
rect1.origin.y = 0;
UIImageView *dot =[[UIImageView alloc] initWithFrame:CGRectMake(rect1.origin.x,rect1.origin.y,rect1.size.width,rect1.size.height)];
dot.image = [UIImage imageNamed:#"miter"];
[self addSubview:dot];
OverlayView *overlay = [[OverlayView alloc] initWithFrame:CGRectMake(rect1.origin.x,rect1.origin.y,rect1.size.width,rect1.size.height)];
[overlay setBackgroundColor:[UIColor clearColor]];
[self addSubview:overlay];
[self bringSubviewToFront:overlay];
}
return self;
}
#end
Set the userInteractionEnabled property to YES. The default for UIViews is YES, but for UIImageViews is NO so you have to explicitly turn it on.

How to create two strokes of different color using one UIBezierPath

I am trying to draw lines on a UIView with finger and it worked fine with one color. If I try to change the color and draw again, the previous UIBezierPath color is also changed to new color. So, I am unable to draw a different color line keeping the previous color line on the UIView
I set all the properties(path,linecolor) as nonatomic and strong in my UIView
For reference:
my first draw:
my second draw:
my third draw:
After I choose the color, I change the stroke color of my UIView in color picker delegate method:
#pragma mark - FCColorPickerViewControllerDelegate Methods
-(void)colorPickerViewController:(FCColorPickerViewController *)colorPicker didSelectColor:(UIColor *)color {
self.drawView.lineColor = color; //this works fine
// self.drawView.path=[UIBezierPath bezierPath]; tried this to create new bezier path with new color, but this erases the olde bezier path and return new
// [self.drawView.lineColor setStroke]; tried this
// [self.drawView.lineColor setFill]; tried this
[self dismissViewControllerAnimated:YES completion:nil]; //dismiss the color picker
}
Here are my view methods for drawing:
- (id)initWithCoder:(NSCoder *)aDecoder
{
if (self = [super initWithCoder:aDecoder])
{
[self setBackgroundColor:[UIColor whiteColor]];
path = [UIBezierPath bezierPath];
[path setLineWidth:self.lineWidth];
}
return self;
}
- (void)drawRect:(CGRect)frame
{
[self.lineColor setStroke];
[path stroke];
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
dispatch_async(dispatch_get_main_queue(), ^{
UITouch *touch = [touches anyObject];
CGPoint p = [touch locationInView:self];
[path moveToPoint:p];
});
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
dispatch_async(dispatch_get_main_queue(), ^{
UITouch *touch = [touches anyObject];
CGPoint p = [touch locationInView:self];
[path addLineToPoint:p];
[self setNeedsDisplay];
});
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
[self touchesMoved:touches withEvent:event];
}
I tried this: adding the old bezier paths to the array and redraw them, but it didnt work , this time I cant make another bezier path with new color.:
- (void)drawRect:(CGRect)frame // (5)
{
//load the path from array
for (int i = 0; i < [pathArray count]; i++){
NSLog(#"Path: %#",[pathArray objectAtIndex:0]);
NSLog(#"Color: %#",[pathArray objectAtIndex:1]);
UIBezierPath *oldpath = [pathArray objectAtIndex:0];
//color
[[pathArray objectAtIndex:1] setStroke];
//path
[oldpath stroke];
}
UIBezierPath *newPath = [self pathForCurrentLine];
if (newPath)
{
// set the width, color, etc, too, if you want
[lineColor setStroke];
[newPath stroke];
}
}
- (UIBezierPath*)pathForCurrentLine {
if (CGPointEqualToPoint(startPoint, CGPointZero) && CGPointEqualToPoint(endPoint, CGPointZero)){
return nil;
}
UIBezierPath *newpath = [UIBezierPath bezierPath];
[newpath moveToPoint:startPoint];
[newpath addLineToPoint:endPoint];
return newpath;
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
dispatch_async(dispatch_get_main_queue(), ^{
UITouch *touch = [touches anyObject];
CGPoint p = [touch locationInView:self];
[path moveToPoint:p];
startPoint=p;
});
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
dispatch_async(dispatch_get_main_queue(), ^{
UITouch *touch = [touches anyObject];
CGPoint p = [touch locationInView:self];
[path addLineToPoint:p]; // (4)
endPoint=p;
[self setNeedsDisplay];
});
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
[self touchesMoved:touches withEvent:event];
[pathArray addObject:path];
[pathArray addObject:lineColor];
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
[self touchesEnded:touches withEvent:event];
}
You actually can't stroke different parts of the same path in different colors.
You can think of the drawing context as a state machine. Each time you issue a draw command (such as calling stroke on a UIBezierPath) it will check the current stroke/fill color and use that to perform the draw. To create multiple strokes in different colors you would need to have multiple paths to stroke, and set the stroke color in between each invocation of stroke.
- (void)drawRect:(CGRect)frame {
UIBezierPath* pathOne = // create path one
UIBezierPath* pathTwo = // create path two
UIBezierPath* pathThree = // create path three
[[UIColor redColor] setStroke];
[pathOne stroke];
[[UIColor greenColor] setStroke];
[pathTwo stroke];
[[UIColor blueColor] setStroke];
[pathThree stroke];
}

Black background in UIView?

I followed a tutorial online to draw in a subclassed UIView. The tutorial showed a UIView with a white background, I fixed this by simply changing the super's bg color. The problem is, when touches end, the background does not remain clear. I have no idea. I simply tried setting the fill color to [uicolor clearcolor]; unsuccessfully. Here is the code I am using:
#implementation CachedLIView
{
UIBezierPath *path;
UIImage *incrementalImage; // (1)
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
if (self = [super initWithCoder:aDecoder])
{
[self setMultipleTouchEnabled:NO];
[self setBackgroundColor:[UIColor whiteColor]];
path = [UIBezierPath bezierPath];
[path setLineWidth:2.0];
}
return self;
}
- (void)drawRect:(CGRect)rect
{
[incrementalImage drawInRect:rect]; // (3)
[path stroke];
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint p = [touch locationInView:self];
[path moveToPoint:p];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint p = [touch locationInView:self];
[path addLineToPoint:p];
[self setNeedsDisplay];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event // (2)
{
UITouch *touch = [touches anyObject];
CGPoint p = [touch locationInView:self];
[path addLineToPoint:p];
[self drawBitmap]; // (3)
[self setNeedsDisplay];
[path removeAllPoints]; //(4)
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
[self touchesEnded:touches withEvent:event];
}
- (void)drawBitmap // (3)
{
UIGraphicsBeginImageContextWithOptions(self.bounds.size, NO, 0.0);
[[UIColor blackColor] setStroke];
if (!incrementalImage) // first draw; paint background white by ...
{
UIBezierPath *rectpath = [UIBezierPath bezierPathWithRect:self.bounds]; // enclosing bitmap by a rectangle defined by another UIBezierPath object
[[UIColor clearColor] setFill];
[rectpath fill]; // filling it with white
}
[incrementalImage drawAtPoint:CGPointZero];
[path stroke];
incrementalImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}
Put the following in your view -(id) initWithFrame method:
self = [super initWithFrame:frame];
if (self) {
self.backgroundColor = [UIColor clearColor];
}
return self;
That way your view subclass will have a transparent background = no black when drawing using bezier, CGContext… methods or anything else.

How to erase the drawing using button click?

I have some troubles with drawing app in IOS. I have created the free hand drawing with the help of some tutorials. But I found some difficulties in erasing the drawing. In my app, I have button with eraser as background image. After I clicked the eraser button, when I swipes over the drawing, it will erase the drawing wherever I swipes. Can anyone help me to do this.
Thanks in advance.
Given below is my code:
#implementation LinearInterpView
{
UIBezierPath *path;
}
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
return self;
}
-(id)initWithCoder:(NSCoder *)aDecoder {
if(self = [super initWithCoder:aDecoder]) {
[self setMultipleTouchEnabled:YES];
[self setBackgroundColor:[UIColor whiteColor]];
path=[UIBezierPath bezierPath];
[path setLineWidth:2.0];
}
return self;
}
-(void)drawRect:(CGRect)rect{
[[UIColor blackColor] setStroke];
[path stroke];
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch=[touches anyObject];
CGPoint p=[touch locationInView:self];
[path moveToPoint:p];
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch=[touches anyObject];
CGPoint p=[touch locationInView:self];
[path addLineToPoint:p];
[self setNeedsDisplay];
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
[self touchesMoved:touches withEvent:event];
}
-(void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event{
[self touchesEnded:touches withEvent:event];
}
// This is the button action to erase the drawing.
- (IBAction)erase:(id)sender {
CGContextRef cgref=UIGraphicsGetCurrentContext();
CGContextSetBlendMode(cgref, kCGBlendModeClear);
}
Kindly clear me, what mistake I did.
So by drawing you mean you have drew lines on the screen say with some color you can do the same by setting white color and alpha 1 so that white lines replace the existing colored lines. A better tutorial here . This also seemed important.
First of all your logic should be make a layer on ImageView.
then you can draw on that layer then pass white color to erase.
It'll look like erase and your view will look like according to requirement.
That will surly work.
Try this to erase drawing in iOS:
- (IBAction)eraserPressed:(id)sender {
red = 255.0/255.0;
green = 255.0/255.0;
blue = 255.0/255.0;
opacity = 1.0;
}
why not you implement the same logic in the erasor button as you did in the draw button. just make the default color of the stroke in the eraser as white color or what ever color your background is.

Draw swipe with UIBezierPath in UIImageView

I am trying to draw something in UIImageView..I am able to draw without problems in UIView using the following code:
#implementation DrawView
{
UIBezierPath *path;
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
if (self = [super initWithCoder:aDecoder])
{
[self setMultipleTouchEnabled:NO];
[self setBackgroundColor:[UIColor whiteColor]];
path = [UIBezierPath bezierPath];
[path setLineWidth:2.0];
}
return self;
}
- (void)drawRect:(CGRect)rect
{
[[UIColor blackColor] setStroke];
[path stroke];
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint p = [touch locationInView:self];
[path moveToPoint:p];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint p = [touch locationInView:self];
[path addLineToPoint:p]; // (4)
[self setNeedsDisplay];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
[self touchesMoved:touches withEvent:event];
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
[self touchesEnded:touches withEvent:event];
}
This skips imageview.. What I need is the opposite. I wanna draw in a uiimageview and skip uiview.. How can I do that? Thanks in advance..
The drawing surface needs to be a parent in the hierarchy of this view set. That way you can draw on the parent view, while the image lies underneath.
-Main View
--Drawing View
---UIImageView

Resources