Why touchesbegan: never gets called on a UIView after UIPinchGestureRecognizer is used? - ios

I'm developing a drawing app which lets user to draw a UIbezierpath on a UIView (InsideView) subviewed by it's superView (self.view). I'm using conventional drawing codes in the InsideView like touchesBegan:, touchesMoved:, touchesEnded:and I'm handling pinch zoom code handlePinch:(UIPinchGestureRecognizer *)recognizer: inside the viewController class (which is InsideView's superView).
When I draw first, I can do the pinch zoom after it but when I do the pinch zoom first, the drawing code (touchesBegan etc) doesn't get called after UIPinchGestureRecognizer is fired and it doesnt draw anything on InsideView. What am I doing wrong? Thanks in advance.
//Code in the InsideView (which is a UIView subclass and is a subview for self.view)
- (id)initWithFrame:(CGRect)frame{
self = [super initWithFrame:frame];
if (self) {
path = [UIBezierPath bezierPath];
[path setLineWidth:7.0];
pointsArray = [NSMutableArray array];
finish = NO;
}
return self;
}
-(void)drawRect:(CGRect)rect{
[[UIColor redColor] setStroke];
[path stroke];
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch = [touches anyObject];
CGPoint p = [touch locationInView:self];
[path moveToPoint:p];
[pointsArray addObject:[NSValue valueWithCGPoint:p]];
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch = [touches anyObject];
CGPoint p = [touch locationInView:self];
[path addLineToPoint:p];
[self setNeedsDisplay];
[pointsArray addObject:[NSValue valueWithCGPoint:p]];
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
[path closePath];
//Smoothing the path.
path.miterLimit=-10;
[self setNeedsDisplay];
finish = YES;
}
//Code in the viewController (InsideView's superView)
- (void)viewDidLoad {
[super viewDidLoad];
InsideView* sV = [InsideView new];
[sV setFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
[self.view addSubview:sV];
//Pinch
UIPinchGestureRecognizer*pinch = [[UIPinchGestureRecognizer alloc]initWithTarget:self action:#selector(handlePinch:)];
pinch.delegate =self;
[sV addGestureRecognizer:pinch];
}
- (void)handlePinch:(UIPinchGestureRecognizer *)recognizer{
if ([recognizer numberOfTouches] < 2)
return;
if (recognizer.state == UIGestureRecognizerStateBegan) {
lastScale = 1.0;
lastPoint = [recognizer locationInView:self.view];
}
// Scale
CGFloat scale = 1.0 - (lastScale - recognizer.scale);
[sV.layer setAffineTransform:
CGAffineTransformScale([sV.layer affineTransform],
scale,
scale)];
lastScale = recognizer.scale;
// Translate
CGPoint point = [recognizer locationInView:self.view];
[sV.layer setAffineTransform:
CGAffineTransformTranslate([sV.layer affineTransform],
point.x - lastPoint.x,
point.y - lastPoint.y)];
lastPoint = [recognizer locationInView:self.view];
}
-(void)handlePinchWithGestureRecognizer:(UIPinchGestureRecognizer *)pinchGestureRecognizer{
sV.transform = CGAffineTransformScale(sV.transform, pinchGestureRecognizer.scale, pinchGestureRecognizer.scale);
pinchGestureRecognizer.scale = 1.0;
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{
return YES;
}

I've developed a UIViewController where I used them together without any problem,s maybe a solution could be to move this logic from UIView to the UIViewController. So at viewDidLoad you can define the pinch recognizer like this
UIPinchGestureRecognizer* pinch = [[UIPinchGestureRecognizer alloc]initWithTarget:self action:#selector(handlePinch:)];
[self.view addGestureRecognizer:pinch];
of course also the method handlePinch: should be moved to the UIViewController, together with touchesBegan:withEvent:,
touchesMoved:withEvent:, touchesEnded:withEvent: methods
what you should change in these methods is that you should change
self
to
self.yourSpecificCurrentUIView

Related

Objective c - transparent draw on view (UIVisualEffectView)

I want to create a transparent draw on a view (like the red draw inside the yellow view).
I have some view (UIVisualEffectView - the yellow view) and i want to draw with the finger, and it will looks like we "erase" it.
I created a subclass of UIVisualEffectView and tried to draw, but its not working so well..
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
for (UITouch *touch in touches)
{
CGPoint location = [touch locationInView:self];
CGFloat drawSize = 45;
CGRect rect = CGRectMake(location.x, location.y, drawSize, drawSize);
[self test:rect];
}
}
-(void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
for (UITouch *touch in touches)
{
CGPoint location = [touch locationInView:self];
CGFloat drawSize = 45;
[self test:CGRectMake(location.x, location.y, drawSize, drawSize)];
}
}
-(void)test:(CGRect)rect
{
if (!self.mask)
{
self.mask = [[UIView alloc]initWithFrame:self.bounds];
self.mask.clipsToBounds = YES;
self.mask.backgroundColor = [UIColor clearColor];
}
if (!self.fillLayer)
{
self.fillLayer = [CAShapeLayer layer];
self.fillLayer.fillRule = kCAFillRuleEvenOdd;
self.fillLayer.fillColor = [UIColor greenColor].CGColor;
}
if (!self.outerbezierPath)
{
self.outerbezierPath = [UIBezierPath bezierPathWithRoundedRect:self.bounds cornerRadius:0];
self.outerbezierPath.usesEvenOddFillRule = NO;
}
self.innerCirclepath = [UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:rect.size.height * 0.5];
[self.outerbezierPath appendPath:self.innerCirclepath];
[self.fillLayer removeFromSuperlayer];
self.fillLayer.path = self.outerbezierPath.CGPath;
[self.mask.layer addSublayer:self.fillLayer];
self.maskView = self.mask;
}

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 will i remove UIBezierPath from the uiview on button click.?

I am working on digital signature on uiview. i create it normaly by this code, but i am not able to remove bezier path on button click.i am sharing my code PLease look at my code.
#import "Signature.h"
#implementation Signature {
UIBezierPath *path;
UIImage *incrementalImage;
CGPoint pts[5]; // we now need to keep track of the four points of a Bezier segment and the first control point of the next segment
uint ctr;
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
if (self = [super initWithCoder:aDecoder])
{
[self setMultipleTouchEnabled:NO];
[self setBackgroundColor:[UIColor greenColor]];
path = [UIBezierPath bezierPath];
[path setLineWidth:2.0];
}
return self;
}
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self setMultipleTouchEnabled:NO];
path = [UIBezierPath bezierPath];
[path setLineWidth:2.0];
}
return self;
}
- (void)drawRect:(CGRect)rect
{
[incrementalImage drawInRect:rect];
[path stroke];
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
ctr = 0;
UITouch *touch = [touches anyObject];
pts[0] = [touch locationInView:self];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint p = [touch locationInView:self];
ctr++;
pts[ctr] = p;
if (ctr == 4)
{
pts[3] = CGPointMake((pts[2].x + pts[4].x)/2.0, (pts[2].y + pts[4].y)/2.0); // move the endpoint to the middle of the line joining the second control point of the first Bezier segment and the first control point of the second Bezier segment
[path moveToPoint:pts[0]];
[path addCurveToPoint:pts[3] controlPoint1:pts[1] controlPoint2:pts[2]]; // add a cubic Bezier from pt[0] to pt[3], with control points pt[1] and pt[2]
[self setNeedsDisplay];
// replace points and get ready to handle the next segment
pts[0] = pts[3];
pts[1] = pts[4];
ctr = 1;
}
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
[self drawBitmap];
[self setNeedsDisplay];
[path removeAllPoints];
ctr = 0;
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(#"hello");
[self touchesEnded:touches withEvent:event];
}
- (void)drawBitmap
{
UIGraphicsBeginImageContextWithOptions(self.bounds.size, YES, 0.0);
if (!incrementalImage) // first time; paint background white
{
UIBezierPath *rectpath = [UIBezierPath bezierPathWithRect:self.bounds];
[[UIColor greenColor] setFill];
[rectpath fill];
}
[incrementalImage drawAtPoint:CGPointZero];
[[UIColor blackColor] setStroke];
[path stroke];
incrementalImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}
- (void)erase {
NSLog(#"Erase Testing...");
// self->path = nil; //Set current path nil
//path = [UIBezierPath bezierPath];
// [self setNeedsDisplay];
}
I call erase method on unbutton click from another class, but I am not able to remove uibezeripath.
Try using this to remove the UIBezierPath,
[path removeAllPoints];
If you use CAShapeLayer *rectLayer = [[CAShapeLayer alloc] init]; aswell then call this line too,
[rectLayer removeFromSuperlayer];
Check how i am using this in one of my project if it can help,
- (void)drawRect:(CGRect)rect {
[_path stroke];
}
- (id)initWithFrame:(CGRect)frame{
self = [super initWithFrame: frame];
if (self) {
// set Multiple touches Enabled to false for allow only single touch.
[self setMultipleTouchEnabled: NO];
_path = [UIBezierPath bezierPath];
// set Line width.
[_path setLineWidth:2.0];
}
return self;
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
isMouseswipped = NO; //for touches eneded to make dot
ctr = 0;
UITouch *myTouch = [touches anyObject];
pts[0] = [myTouch locationInView: self];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
isMouseswipped = YES; //for touches eneded to make dot
CustomSignatureViewController *csvc = [[CustomSignatureViewController alloc]init];
UITouch *touch = [touches anyObject];
CGPoint p = [touch locationInView: self];
ctr++;
pts[ctr] = p;
if (ctr == 4) {
pts[3] = CGPointMake((pts[2].x + pts[4].x)/2.0, (pts[2].y + pts[4].y)/2.0);
[_path moveToPoint: pts[0]];
[_path addCurveToPoint: pts[3] controlPoint1:pts[1] controlPoint2:pts[2]];
[self setNeedsDisplay];
pts[0] = pts[3];
pts[1] = pts[4];
ctr = 1;
csvc.status = 1;
self.status = 1;
}
}
- (void)erase {
_path = nil;
_path = [UIBezierPath bezierPath];
[_path setLineWidth:2.0];
[self setNeedsDisplay];
}
This is custom class which returns uiview to fraw signature. It is subclass of UIView.
So when required signature, i import this class and instantiate it and got uiview object on which i can draw. then capture that view as image using graphics beginimage context!
Update :
In viewcontroller i am calling method like,
-(void)clear : (id)sender{
[signView erase];
signView.status = 0;
}
signView is signature class(as mentioned above)'s object. and erase is public method declared in .h file of signature class. (note : signature class is subclass of uiview so intance of it returns view onject so signView is uiview object)
Hope this will help :)
use this code it will help you
- (void)resetPath {
path = nil;
path = [UIBezierPath bezierPath];
[self setNeedsDisplay];
}
create this method in .h file of your subclass. From your UIViewController class you call this method whenever you need it
- (IBAction)youraction:(id)sender
{
[self.subclassedView resetPath];
}

How to fix the position of an image after moving it?

I am using - (void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event to move an image.How I can fix its position after moving it.I mean I would like when I choose the right position,the image should not move no more.
Add PanGesture to the imageView containing the image.
UIPanGestureRecognizer *panRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(panDetected:)];
[imageView addGestureRecognizer:panRecognizer];
//method to move imageView whereever you want in the view,you can also write conditions to restrict the imageView not to go beyond bounds.
- (void)panDetected:(UIPanGestureRecognizer *)panRecognizer
{
CGPoint translation = [panRecognizer translationInView:self.view];
CGPoint imageViewPosition = imageView.center;
imageViewPosition.x += translation.x;
imageViewPosition.y += translation.y;
imageView.center = imageViewPosition;
[panRecognizer setTranslation:CGPointZero inView:self.view];
//you can use if(panRecognizer.state==UIGestureRecognizerStateEnded){} condition to fix the position of the imageView.
}
you can do like this:
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
UIImageView *view1 =moveImageView;
CGPoint point = [touch locationInView:self.view];
//the right position
if(point.x==?&&point.y==?){
rightPosition = YES;
moveImageView.frame = CGRectMake(point.x, point.y, 50, 50);
}else{
//the UIImageView you want to move
if(!rightPosition){
moveImageView.frame = CGRectMake(point.x, point.y, 50, 50);
}
}
}

How to get the lowest View in touchesMoved:?

I have 3 UIViews: needToDragView, its superView, and self.view. When I use touchesMoved: to drag only my needToDragView(lowest in 3Views). But it can drag all 3View. How to detect the lowest UIView?
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
UIView *dragView = [[touches anyObject] view];
CGPoint newCenter = [[touches anyObject] locationInView:dragView.superview];
dragView.center = newCenter;
}
Try this:
Make sure you set UserInteractionEnabled = YES; for needToDragView.
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
{
UITouch *touch = [touches anyObject];
if ([touch view]== needToDragView) {
// Drag Image Here
}
else {
// NSLog("Wrong");
}
}
UPDATE
Instead of this, you can also use UIPanGestureRecognizer like this:
UIPanGestureRecognizer pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(function:)];
[pan setMinimumNumberOfTouches:1];
[pan setMaximumNumberOfTouches:2];
[needsToDragView addGestureRecognizer:pan];
[needsToDragView setUserInteractionEnabled:YES];
- (void)function:(UIPanGestureRecognizer *)recognizer
{
NSLog(#"Moving View");
mainView= recognizer.view;
CGPoint translatedPoint = [recognizer translationInView:self.view];
if([recognizer state] == UIGestureRecognizerStateBegan)
{
firstX = [mainView center].x;
firstY = [mainView center].y;
}
translatedPoint = CGPointMake(firstX+translatedPoint.x, firstY+translatedPoint.y);
[mainView setCenter:translatedPoint];
}

Resources