Draw swipe with UIBezierPath in UIImageView - uiview

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

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

Draw UIBezierPath on UIImageView for image crop

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

Crop image when draw line with hand

I have a Image view added on a view. View is using UIBezierPath and touch responder methods for drawing line upon dragging on screen. Now I would like to clip the part of image that are below the line. How can I achieve this.
I use below methods to draw line
- (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];
}
Try this:
#property (strong, nonatomic) UIBezierPath *bezierPath;
//...
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint p = [touch locationInView:self];
//...
self.bezierPath = [UIBezierPath bezierPath];
[self.bezierPath moveToPoint:p];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint p = [touch locationInView:self];
//...
[self.bezierPath addLineToPoint:p];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
[self touchesMoved:touches withEvent:event];
// Here you can call -croppedImage; method and get the image you need.
}
- (UIImage *)croppedImage
{
[self.bezierPath closePath];
UIImage *image = //Your initial image;
CGSize imageSize = image.size;
CGRect imageRect = CGRectMake(0, 0, imageSize.width, imageSize.height);
UIGraphicsBeginImageContextWithOptions(imageSize, NO, [[UIScreen mainScreen] scale]);
// Create the clipping path and add it
[self.bezierPath addClip];
[image drawInRect:imageRect];
UIImage *croppedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return croppedImage;
}
This is a fast written code, so can be possible mistakes. Sorry for that :)

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.

Resources