Stretchy UIBezierPath line? - objective-c

I want to draw a straight line with my finger that automatically sizes based upon how far away I am from the point of origin.
So if I touch the screen in the middle and slide my finger out a line appears to 'stretch' and pivot around the point of orgin as my finger moves on the screen. WHhen I lift my finger. The Destination Point should finalize and create a line. I can drag my finger across the screen and 'Draw' on the screen but that's not what I am wanting to do.
I thought UIBeizerPath moveToPoint would help but it just messes things up.
What am I doing wrong?
- (id)initWithFrame:(CGRect)frame
{
//default line properties
myPath=[[UIBezierPath alloc]init];
myPath.lineCapStyle=kCGLineCapRound;
myPath.miterLimit=0;
myPath.lineWidth=lineWidth;
brushPattern=[UIColor blackColor];
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
CGPoint curPoint = [[touches anyObject] locationInView:self];
lastPoint = curPoint;
[myPath moveToPoint:lastPoint];
[pathArray addObject:myPath];
[self setNeedsDisplay];
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
CGPoint curPoint = [[touches anyObject] locationInView:self];
myPath.lineWidth=lineWidth;
brushPattern=[UIColor redColor]; //red to show it hasn't been added yet.
[myPath moveToPoint:tempPoint];
[self setNeedsDisplay];
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
CGPoint curPoint = [[touches anyObject] locationInView:self];
myPath.lineWidth=lineWidth;
brushPattern=[UIColor blackColor]; //finalize the line with black color
[myPath addLineToPoint:curPoint];
[self setNeedsDisplay];
}

Here's one concept. Draws a line from where you start dragging your finger until where you let go, animating it as you drag your finger around. It does this by making a CAShapeLayer, resetting the path as you move your finger around.
This should demonstrate the basic idea:
- (void)viewDidLoad {
[super viewDidLoad];
UIPanGestureRecognizer *gesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(handlePanGesture:)];
[self.view addGestureRecognizer:gesture];
}
- (CAShapeLayer *)createShapeLayer:(UIView *)view {
CAShapeLayer *shapeLayer = [[CAShapeLayer alloc] init];
shapeLayer.fillColor = [UIColor clearColor].CGColor;
shapeLayer.strokeColor = [UIColor redColor].CGColor;
shapeLayer.lineWidth = 3.0;
[view.layer addSublayer:shapeLayer];
return shapeLayer;
}
- (void)handlePanGesture:(UIPanGestureRecognizer *)gesture {
static CAShapeLayer *shapeLayer;
static CGPoint origin;
if (gesture.state == UIGestureRecognizerStateBegan) {
shapeLayer = [self createShapeLayer:gesture.view];
origin = [gesture locationInView:gesture.view];
} else if (gesture.state == UIGestureRecognizerStateChanged) {
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:origin];
CGPoint location = [gesture locationInView:gesture.view];
[path addLineToPoint:location];
shapeLayer.path = path.CGPath;
} else if (gesture.state == UIGestureRecognizerStateEnded ||
gesture.state == UIGestureRecognizerStateFailed ||
gesture.state == UIGestureRecognizerStateCancelled) {
shapeLayer = nil;
}
}
Or, in Swift 3:
override func viewDidLoad() {
super.viewDidLoad()
let pan = UIPanGestureRecognizer(target: self, action: #selector(handlePan(_:)))
view.addGestureRecognizer(pan)
}
private func createShapeLayer(for view: UIView) -> CAShapeLayer {
let shapeLayer = CAShapeLayer()
shapeLayer.fillColor = UIColor.clear.cgColor
shapeLayer.strokeColor = UIColor.red.cgColor
shapeLayer.lineWidth = 3.0
view.layer.addSublayer(shapeLayer)
return shapeLayer
}
private var shapeLayer: CAShapeLayer!
private var origin: CGPoint!
func handlePan(_ gesture: UIPanGestureRecognizer) {
if gesture.state == .began {
shapeLayer = createShapeLayer(for: gesture.view!)
origin = gesture.location(in: gesture.view)
} else if gesture.state == .changed {
let path = UIBezierPath()
path.move(to: origin)
path.addLine(to: gesture.location(in: gesture.view))
shapeLayer.path = path.cgPath
} else if gesture.state == .ended || gesture.state == .failed || gesture.state == .cancelled {
shapeLayer = nil
}
}
If you don't use CAShapeLayer, but you want to keep track of previous paths, you'll have to maintain an array for those old paths, and build a path that consists of all of the old paths, perhaps something like:
#interface CustomView ()
#property (nonatomic) CGPoint originPoint;
#property (nonatomic) CGPoint currentPoint;
#property (nonatomic) NSMutableArray *previousPaths;
#end
#implementation CustomView
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
[self configure];
}
return self;
}
- (id)init {
return [self initWithFrame:CGRectZero];
}
- (id)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if (self) {
[self configure];
}
return self;
}
- (void)configure {
_previousPaths = [[NSMutableArray alloc] init];
}
- (void)drawRect:(CGRect)rect {
[[UIColor redColor] setStroke];
UIBezierPath *drawPath = [UIBezierPath bezierPath];
drawPath.lineCapStyle = kCGLineCapRound;
drawPath.miterLimit = 0;
drawPath.lineWidth = 3.0;
for (UIBezierPath *path in self.previousPaths)
[drawPath appendPath:path];
UIBezierPath *path = [self pathForCurrentLine];
if (path)
[drawPath appendPath:path];
[drawPath stroke];
}
- (UIBezierPath *)pathForCurrentLine {
if (CGPointEqualToPoint(self.originPoint, CGPointZero) && CGPointEqualToPoint(self.currentPoint, CGPointZero))
return nil;
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:self.originPoint];
[path addLineToPoint:self.currentPoint];
return path;
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
self.originPoint = [[touches anyObject] locationInView:self];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
if ([event respondsToSelector:#selector(predictedTouchesForTouch:)]) {
touch = [[event predictedTouchesForTouch:touch] lastObject] ?: touch;
}
self.currentPoint = [touch locationInView:self];
[self setNeedsDisplay];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
self.currentPoint = [[touches anyObject] locationInView:self];
[self.previousPaths addObject:[self pathForCurrentLine]];
self.originPoint = self.currentPoint = CGPointZero;
[self setNeedsDisplay];
}
#end

UIBezierPath is building a path from your instructions. Imagine a pen. When you say, "moveToPoint:" it moves the pen to that point. When you say "lineToPoint:" it puts the pen down and moves it from the current location to the new point. And so on.
To get the effect you desire, you will need to create a new path whenever the touches move, drawing a line from the original point to the current touch position.

Related

Implement touch cancel on UIButton

I have a subclassed UIButton that I made into an irregular shape (Parallelogram) where I override the touch events so it will only accept touch events inside the shape
How can I implement a touch event like a normal UIButton where I can cancel a touch event upon tapping and dragging the finger outside the UIButton to cancel a touch. For my current code, If I drag my finger inside the button, it calls the touchesCancelled event. I am using the TouchUpInside event for performing methods in the UIButton. Here is my code:
- (id)initWithFrame:(CGRect)frame withAngle:(AngleType)angle andColor:(UIColor *)color
{
if ((self = [super initWithFrame:frame])){
[self setImage:[Utils imageWithColor:color andSize:frame.size] forState:UIControlStateNormal];
_path = [UIBezierPath new];
self.angleType = angle;
switch (angle) {
case AngleLeft:
{
[_path moveToPoint:CGPointMake(0, elementHeight(self))];
[_path addLineToPoint:CGPointMake(elementWidth(self), elementHeight(self))];
[_path addLineToPoint:CGPointMake(elementWidth(self), 0)];
[_path addLineToPoint:CGPointMake(15, 0)];
[_path addLineToPoint:CGPointMake(0, elementHeight(self))];
}
break;
case AngleRight:
{
[_path moveToPoint:CGPointMake(0, 0)];
[_path addLineToPoint:CGPointMake(0, elementHeight(self))];
[_path addLineToPoint:CGPointMake(elementWidth(self),elementHeight(self))];
[_path addLineToPoint:CGPointMake(elementWidth(self) - 15, 0)];
[_path addLineToPoint:CGPointMake(0, 0)];
}
break;
case AngleBoth:
{
[_path moveToPoint:CGPointMake(15, 0)];
[_path addLineToPoint:CGPointMake(0, elementHeight(self))];
[_path addLineToPoint:CGPointMake(elementWidth(self) - 15, elementHeight(self))];
[_path addLineToPoint:CGPointMake(elementWidth(self), 0)];
[_path addLineToPoint:CGPointMake(15, 0)];
}
break;
default:
break;
}
CAShapeLayer *mask = [CAShapeLayer new];
mask.frame = self.bounds;
mask.path = _path.CGPath;
self.layer.mask = mask;
}
return self;
}
- (void)setText:(NSString *)text withAlignment:(NSTextAlignment)alignment
{
_buttonLabel = [[UILabel alloc] init];
_buttonLabel.font = FONT_Helvetica_Neue(14);
_buttonLabel.textColor = [UIColor whiteColor];
_buttonLabel.text = text;
_buttonLabel.textAlignment = alignment;
if (alignment == NSTextAlignmentLeft) {
_buttonLabel.frame = CGRectMake(15 + 10, 0, elementWidth(self), elementHeight(self));
} else if (alignment == NSTextAlignmentRight) {
_buttonLabel.frame = CGRectMake(0, 0, elementWidth(self) - 15 - 10, elementHeight(self));
} else if (alignment == NSTextAlignmentCenter) {
_buttonLabel.frame = CGRectMake(0, 0, elementWidth(self), elementHeight(self));
}
[self addSubview:_buttonLabel];
}
- (void)setBackgroundColor:(UIColor *)backgroundColor
{
[self setImage:[Utils imageWithColor:backgroundColor andSize:self.frame.size] forState:UIControlStateNormal];
CAShapeLayer *mask = [CAShapeLayer new];
mask.frame = self.bounds;
mask.path = _path.CGPath;
self.layer.mask = mask;
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [[event allTouches] anyObject];
CGPoint touchLocation = [touch locationInView:self];
if ([_path containsPoint:touchLocation]) {
NSLog(#"Inside!");
self.highlighted = YES;
//[self sendActionsForControlEvents:UIControlEventTouchUpInside];
}
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [[event allTouches] anyObject];
CGPoint touchLocation = [touch locationInView:self];
self.highlighted = NO;
if ([_path containsPoint:touchLocation]) {
[self sendActionsForControlEvents:UIControlEventTouchUpInside];
}
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [[event allTouches] anyObject];
CGPoint touchLocation = [touch locationInView:self];
if ([_path containsPoint:touchLocation]) {
NSLog(#"...");
self.highlighted = YES;
}
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [[event allTouches] anyObject];
CGPoint touchLocation = [touch locationInView:self];
self.highlighted = NO;
if ([_path containsPoint:touchLocation]) {
[self sendActionsForControlEvents:UIControlEventTouchUpInside];
}
}
Instead of trying to implement custom touch events handling, please override the hitTest:withEvent: method. Please refer to this tutorial which I believe describes your case. And this is another example.

UIImage Resize drawRect Filling Background with Black

I have a class called MNTRectangle which is a subclass of UIImage. I have overridden the drawRect method of this class to draw a border on the image (using its frame). I have it so when the user starts a panning/dragging gesture on a class called DocumentView (subclass of UIView) it adds an instance of MNTRectangle as a subview to the instance of DocumentView. As the user continues to drag the MNTRectangle is resized. The problem is that the MNTRectangle is appearing as solid black in the end, I have tried clearing the graphics context as well as saving the context before drawing the border and restoring the context after drawing the border. No matter what I seem to try I cannot get the MNTRectangle to clear and just show the border on resize.
Here is the code for my MNTRectangle class:
#implementation MNTRectangle
- (id)init
{
self = [super init];
if (self)
{
[self setup];
}
return self;
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self)
{
[self setup];
}
return self;
}
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
[self setup];
}
return self;
}
- (void)setup
{
[self setBackgroundColor:[UIColor clearColor]];
}
- (void)drawRect:(CGRect)rect
{
// Get graphics context
CGContextRef context = UIGraphicsGetCurrentContext();
// CGContextSaveGState(context);
// CGContextClearRect(context, rect);
// Draw border
CGContextSetLineWidth(context, 4.0);
[[UIColor blackColor] setStroke];
CGContextStrokeRect(context, rect);
// CGContextRestoreGState(context);
}
Here is the code in DocumentView for the panning/dragging handling on the UIView:
-(id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
_panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(handlePan:)];
}
return self;
}
- (void)handlePan:(UIPanGestureRecognizer *)sender
{
if ([sender state] == UIGestureRecognizerStateBegan)
{
_startPanPoint = [sender locationInView:self];
MNTRectangle *rectangle = [[MNTRectangle alloc] initWithFrame:CGRectMake(_startPanPoint.x, _startPanPoint.y, 1, 1)];
[_objects addObject:rectangle];
_currentPanObject = rectangle;
[self addSubview:rectangle];
}
else if ([sender state] == UIGestureRecognizerStateChanged)
{
CGPoint endPanPoint = [sender locationInView:self];
float height = fabsf(endPanPoint.y - _startPanPoint.y);
float width = fabsf(endPanPoint.x - _startPanPoint.x);
float x = MIN(_startPanPoint.x, endPanPoint.x);
float y = MIN(_startPanPoint.y, endPanPoint.y);
MNTRectangle *rectangle = (MNTRectangle *)_currentPanObject;
[rectangle setFrame:CGRectMake(x, y, width, height)];
}
else if ([sender state] == UIGestureRecognizerStateEnded)
{
CGPoint endPanPoint = [sender locationInView:self];
float height = fabsf(endPanPoint.y - _startPanPoint.y);
float width = fabsf(endPanPoint.x - _startPanPoint.x);
float x = MIN(_startPanPoint.x, endPanPoint.x);
float y = MIN(_startPanPoint.y, endPanPoint.y);
MNTRectangle *rectangle = (MNTRectangle *)_currentPanObject;
[rectangle setFrame:CGRectMake(x, y, width, height)];
}
}
Any help with this would be greatly appreciated.
I finally figured it out. In my handlePan: method after I set the frame of the rectangle I was missing [rectangle setNeedsDisplay];.
Now my DocumentView code looks like this:
-(id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
_panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(handlePan:)];
}
return self;
}
- (void)handlePan:(UIPanGestureRecognizer *)sender
{
if ([sender state] == UIGestureRecognizerStateBegan)
{
_startPanPoint = [sender locationInView:self];
MNTRectangle *rectangle = [[MNTRectangle alloc] initWithFrame:CGRectMake(_startPanPoint.x, _startPanPoint.y, 1, 1)];
[_objects addObject:rectangle];
_currentPanObject = rectangle;
[self addSubview:rectangle];
}
else if ([sender state] == UIGestureRecognizerStateChanged)
{
CGPoint endPanPoint = [sender locationInView:self];
float height = fabsf(endPanPoint.y - _startPanPoint.y);
float width = fabsf(endPanPoint.x - _startPanPoint.x);
float x = MIN(_startPanPoint.x, endPanPoint.x);
float y = MIN(_startPanPoint.y, endPanPoint.y);
MNTRectangle *rectangle = (MNTRectangle *)_currentPanObject;
[rectangle setFrame:CGRectMake(x, y, width, height)];
[rectangle setNeedsDisplay];
}
else if ([sender state] == UIGestureRecognizerStateEnded)
{
CGPoint endPanPoint = [sender locationInView:self];
float height = fabsf(endPanPoint.y - _startPanPoint.y);
float width = fabsf(endPanPoint.x - _startPanPoint.x);
float x = MIN(_startPanPoint.x, endPanPoint.x);
float y = MIN(_startPanPoint.y, endPanPoint.y);
MNTRectangle *rectangle = (MNTRectangle *)_currentPanObject;
[rectangle setFrame:CGRectMake(x, y, width, height)];
[rectangle setNeedsDisplay];
}
}
You're gonna want to call [super drawRect:rect]
To clarify for anyone reading, calling [rectangle setNeedsDisplay]; is needed because you're overriding drawRect: and thus this subclass of UIView, MNTRectangle, needs to be told to manually redraw itself after it's internal variables have been changed.
This function is called automatically on standard objects that inherit from UIView, but since you've created a custom object, you'll need to call it manually if you have some special behavior in MNTRectangle that requires a redraw.
Further documentation here.

UIBezierPath to draw straight lines

Here is the relevant .m that I am currently using.
- (void)drawRect:(CGRect)rect
{
[[UIColor redColor] setStroke];
for (UIBezierPath *_path in pathArray)
[_path strokeWithBlendMode:kCGBlendModeNormal alpha:1.0];
}
#pragma mark -
#pragma mark - Touch Methods
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
myPath=[[UIBezierPath alloc]init];
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
if([ud objectForKey:#"lineThickness"] == nil) {
myPath.lineWidth=5;
}
else {
float thicknessFloat = [ud floatForKey:#"lineThickness"];
myPath.lineWidth= 10. * thicknessFloat;
}
UITouch *mytouch=[[touches allObjects] objectAtIndex:0];
[myPath moveToPoint:[mytouch locationInView:self]];
[pathArray addObject:myPath];
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *mytouch=[[touches allObjects] objectAtIndex:0];
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
if([ud objectForKey:#"lineThickness"] == nil) {
myPath.lineWidth=5;
}
else {
float thicknessFloat = [ud floatForKey:#"lineThickness"];
myPath.lineWidth= 10. * thicknessFloat;
}
[myPath addLineToPoint:[mytouch locationInView:self]];
[self setNeedsDisplay];
}
It works great, but since this is tutorial code that is slightly modified by me, I do not know how to approach the problem of wanting to draw lines between two points, and have the framework connect the the points each time a point is added.
Can anyone please point me in a good direction on how to accomplish this please?
The particulars of how to implement this depend upon the effect that you're looking for. If you're just tapping on a bunch of points and want to add them to a UIBezierPath you can do something like the following in your view controller:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *mytouch = [[touches allObjects] objectAtIndex:0];
CGPoint location = [mytouch locationInView:self.view];
// I'm assuming you have a myPath UIBezierPath which is an ivar which is
// initially nil. In that case, we'll check if it's nil and if so, initialize
// it, otherwise, it's already been initialized, then we know we're just
// adding a line segment.
if (!myPath)
{
myPath = [UIBezierPath bezierPath];
[myPath moveToPoint:location];
shapeLayer = [[CAShapeLayer alloc] initWithLayer:self.view.layer];
shapeLayer.lineWidth = 1.0;
shapeLayer.strokeColor = [UIColor redColor].CGColor;
shapeLayer.fillColor = [UIColor clearColor].CGColor;
[self.view.layer addSublayer:shapeLayer];
}
else
{
[myPath addLineToPoint:location];
shapeLayer.path = myPath.CGPath;
}
}
If you wanted something where you can draw with your finger (e.g. dragging your finger draws), then it might look something like:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *mytouch = [[touches allObjects] objectAtIndex:0];
CGPoint location = [mytouch locationInView:self.view];
myPath = [UIBezierPath bezierPath];
[myPath moveToPoint:location];
shapeLayer = [[CAShapeLayer alloc] initWithLayer:self.view.layer];
shapeLayer.lineWidth = 1.0;
shapeLayer.strokeColor = [UIColor redColor].CGColor;
shapeLayer.fillColor = [UIColor clearColor].CGColor;
[self.view.layer addSublayer:shapeLayer];
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *mytouch = [[touches allObjects] objectAtIndex:0];
CGPoint location = [mytouch locationInView:self.view];
[myPath addLineToPoint:location];
shapeLayer.path = myPath.CGPath;
}
I would not use UIBezierPath since it is intended more for drawing curved paths.
The most efficient way to accomplish this would be to use core graphics draw commands within drawRect while using an array to store the points you want to draw; this array is appended to in your touch methods.
- (void)drawRect:(CGRect)rect {
CGContextRef c = UIGraphicsGetCurrentContext();
CGFloat black[4] = {0, 0,
0, 1};
CGContextSetStrokeColor(c, black);
CGContextBeginPath(c);
CGContextMoveToPoint(c, 100, 100);
CGContextAddLineToPoint(c, 100, 200); //call this in a loop that goes through the point array
CGContextStrokePath(c);
}
There is much more information here: Quartz 2D Programming Guide
Hope this helps!

CCPanZoomController + Tappable Sprites

I am creating a zoomable and pan-able map for my game (using CCPanZoomController) . Within this map I would like to have a tappable sprite, and when it is tapped, "do something"…
I can get the two things to work perfectly in separate projects, however when I try to combine them, nothing happens when I tap my sprite.
I have included an image to demonstrate further:
//in my init section
self.isTouchEnabled = YES;
mapBase = [CCSprite spriteWithFile:#"MapBase.png"];
mapBase.anchorPoint = ccp(0, 0);
[self addChild:mapBase z:-10];
gym = [CCSprite spriteWithFile:#"Gym.png"];
gym.scale = 0.3;
gym.position = ccp(1620, 250);
[self addChild:gym z:1];
CGRect boundingRect = CGRectMake(0, 0, 2499, 1753);
_controller = [[CCPanZoomController controllerWithNode:self] retain];
_controller.boundingRect = boundingRect;
_controller.zoomOutLimit = _controller.optimalZoomOutLimit;
_controller.zoomInLimit = 2.0f;
[_controller enableWithTouchPriority:1 swallowsTouches:YES];
//end of init
-(void) registerWithTouchDispatcher
{
[[[CCDirector sharedDirector] touchDispatcher] addTargetedDelegate:self
priority:0 swallowsTouches:NO];
}
-(BOOL) ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event{
CGPoint touchPoint1 = [touch locationInView:[touch view]];
if (CGRectContainsPoint(gym.boundingBox, touchPoint1)) return YES;
return NO;
}
-(void) ccTouchEnded:(UITouch *)touch withEvent:(UIEvent *)event{
CGPoint touchPoint2 = [touch locationInView:[touch view]];
if (CGRectContainsPoint(gym.boundingBox, touchPoint2)){
CCLOG(#"SPRITE HAS BEEN TAPPED");
}
}
I want to beable to zoom in/out and pan the wholemap (including the ‘Gym’ sprite).
And if the sprite 'Gym' is tapped by the user, then i would like to “do something”.
If anyone can figure this out, I would be extremely grateful!
Thanks.

Eraser for UIBezierPath

I am using UIBezierPath for free hand drawing in an iPad app. I want to apply an eraser to a uibezierpath.
However, I want to only erase the drawing in its path. I cannot use the path color as the background color because I have other elements on the background.
Below is how I am creating my free hand drawings:
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
self.backgroundColor = [UIColor clearColor];
self.opaque = NO;
lineWidths = 10;
brushPattern = [UIColor greenColor];
pathArray = [[NSMutableArray alloc]init];
bufferArray = [[NSMutableArray alloc]init];
self.multipleTouchEnabled = NO;
}
return self;
}
- (void)drawRect:(CGRect)rect {
for (NSMutableDictionary *dictionary in pathArray) {
UIBezierPath *_path = [dictionary objectForKey:#"Path"];
UIColor *_colors = [dictionary objectForKey:#"Colors"];
[_colors setStroke];
_path.lineCapStyle = kCGLineCapRound;
[_path stroke];
}
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
myPath = [[UIBezierPath alloc]init];
myPath.lineWidth = lineWidths;
CGPoint touchPoint = [[touches anyObject] locationInView:self];
UITouch *mytouch = [[touches allObjects] objectAtIndex:0];
[myPath moveToPoint:[mytouch locationInView:self]];
[myPath addLineToPoint:CGPointMake(touchPoint.x, touchPoint.y)];
dict = #{#"Path": myPath, #"Colors": brushPattern};
[pathArray addObject:dict];
[self setNeedsDisplay];
[undoManager registerUndoWithTarget:self selector:#selector(undoButtonClicked) object:nil];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *mytouch = [[touches allObjects] objectAtIndex:0];
[myPath addLineToPoint:[mytouch locationInView:self]];
[self setNeedsDisplay];
}
Store a BOOL value for erase: BOOL _erase;
BOOL eraseButtonIsTapped = ...
if eraseButtonIsTapped {
_erase = yes;
} else{
_erase = NO;
}
When drawing:
[myPath strokeWithBlendMode:_erase?kCGBlendModeClear:kCGBlendModeNormal alpha:1.0f];
Just try this
brushPattern = view.backgroundColor;
This will draw a new line with the color what exactly behind your drawn path. And you can use the same pathArray to do this. So that later on you can implement redo/undo operations too. if you want i could explain you more on this.