Cómo ignorar ciertos puntos UITouch en secuencia multitáctil

Estoy trabajando en una aplicación de dibujo para iPad, estoy proporcionando pantalla completa para dibujar. Así como todos ahora, el usuario puede escribir con su soporte de muñeca o apoyando su mano en la pantalla. Entonces, mi objective es permitir que el usuario escriba libremente con su soporte para la muñeca / mano.

Pero la aplicación solo debe detectar el dibujo de los dedos, o ignorar / rechazar la muñeca y tocar con la mano y eliminarlos

Comencé a trabajar en él, creé un proyecto de ejemplo con multitouch habilitado.

A continuación se muestra mi código

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { mouseSwiped = NO; for (UITouch *touch in touches) { NSString *key = [NSString stringWithFormat:@"%d", (int) touch]; lastPoint = [touch locationInView:self.view]; [touchPaths setObject:[NSValue valueWithCGPoint:lastPoint] forKey:key]; } } - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { mouseSwiped = YES; CGPoint lastPoint3; for (UITouch *touch in touches) { NSString *key = [NSString stringWithFormat:@"%d", (int) touch]; lastPoint = [[touchPaths objectForKey:key] CGPointValue]; currentPoint1 = [touch locationInView:self.view]; NSLog(@"Y:%f",currentPoint1.y); UIGraphicsBeginImageContext(self.view.frame.size); [self.tempDrawImage.image drawInRect:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)]; CGContextSetLineCap(UIGraphicsGetCurrentContext(), kCGLineCapRound); CGContextSetLineWidth(UIGraphicsGetCurrentContext(), brush ); CGContextSetRGBStrokeColor(UIGraphicsGetCurrentContext(), networking, green, blue, 1.0); CGContextSetBlendMode(UIGraphicsGetCurrentContext(),kCGBlendModeNormal); CGContextBeginPath(UIGraphicsGetCurrentContext()); CGContextMoveToPoint(UIGraphicsGetCurrentContext(), lastPoint.x, lastPoint.y); CGContextAddLineToPoint(UIGraphicsGetCurrentContext(), currentPoint1.x, currentPoint1.y); CGContextStrokePath(UIGraphicsGetCurrentContext()); self.tempDrawImage.image = UIGraphicsGetImageFromCurrentImageContext(); [self.tempDrawImage setAlpha:opacity]; UIGraphicsEndImageContext(); [touchPaths setObject:[NSValue valueWithCGPoint:currentPoint1] forKey:key]; } } 

Así que esto funciona bien con cualquier cantidad de toques, pero no entiendo cómo puedo rechazar esos toques de palma / mano al dibujar y solo dibujar, qué dibuja el usuario con su dedo / aguja.

Actualmente si dibujo, obtengo esta cosa, debajo está la image

Aquí he dibujado con mi soporte de manos, puedes ver a continuación "Hola", es un dibujo extraño que ha sucedido. Cómo puedo rechazar esos toques y eliminarlos y solo dibujar hola

Gracias

Ranjit

Una solución es almacenar el toque más alto en touchesBegan de touchesBegan y solo dibujar éste.

Como ya ha señalado, se supone que no debe retener la instancia de UITouch , por lo que recomiendo utilizar una reference débil.

Esto solo dibujará un solo toque. Si desea dibujar los toques de varios dedos, necesita otra forma de filtrar la mano (muchas aplicaciones de dibujo tienen configuraciones de usuario para decirle a la aplicación la pose de la mano, por ejemplo, pero esto es, por supuesto, más complicado).

Aquí hay una idea sobre cómo hacerlo:

 #import <QuartzCore/QuartzCore.h> @interface TViewController () { // We store a weak reference to the current touch that is tracked // for drawing. __weak UITouch* drawingTouch; // This is the previous point we drawed to, or the first point the user tapped. CGPoint touchStartPoint; } @end @interface _TDrawView : UIView { @public CGLayerRef persistentLayer, tempLayer; } -(void)commitDrawing; -(void)discardDrawing; @end @implementation TViewController - (void) loadView { self.view = [[_TDrawView alloc] initWithFrame:[UIScreen mainScreen].bounds]; self.view.opaque = YES; self.view.multipleTouchEnabled = YES; self.view.backgroundColor = [UIColor whiteColor]; } - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { // Start with what we currently have UITouch* topmostTouch = self->drawingTouch; // Find the top-most touch for (UITouch *touch in touches) { CGPoint lastPoint = [touch locationInView:self.view]; if(!topmostTouch || [topmostTouch locationInView:self.view].y > lastPoint.y) { topmostTouch = touch; touchStartPoint = lastPoint; } } // A new finger became the drawing finger, discard any previous // strokes since last touchesEnded if(self->drawingTouch != nil && self->drawingTouch != topmostTouch) { [(_TDrawView*)self.view discardDrawing]; } self->drawingTouch = topmostTouch; } - (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { // Always commit the current stroke to the persistent layer if the user // releases a finger. This could need some tweaking for optimal user experience. self->drawingTouch = nil; [(_TDrawView*)self.view commitDrawing]; [self.view setNeedsDisplay]; } - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { const CGFloat networking=0, green=0, blue=0, brush=1; for (UITouch *touch in touches) { // Find the touch that we track for drawing if(touch == self->drawingTouch) { CGPoint currentPoint = [touch locationInView:self.view]; // Draw stroke first in temporary layer CGContextRef ctx = CGLayerGetContext(((_TDrawView*)self.view)->tempLayer); CGContextSetLineCap(ctx, kCGLineCapRound); CGContextSetLineWidth(ctx, brush ); CGContextSetRGBStrokeColor(ctx, networking, green, blue, 1.0); CGContextSetBlendMode(ctx,kCGBlendModeNormal); CGContextBeginPath(ctx); CGContextMoveToPoint(ctx, touchStartPoint.x, touchStartPoint.y); CGContextAddLineToPoint(ctx, currentPoint.x, currentPoint.y); CGContextStrokePath(ctx); // Update the points so that the next line segment is drawn from where // we left off touchStartPoint = currentPoint; // repaint the layer [self.view setNeedsDisplay]; } } } @end @implementation _TDrawView - (void) finalize { if(persistentLayer) CGLayerRelease(persistentLayer); if(tempLayer) CGLayerRelease(tempLayer); } - (void) drawRect:(CGRect)rect { CGContextRef ctx = UIGraphicsGetCurrentContext(); if(!persistentLayer) persistentLayer = CGLayerCreateWithContext(ctx, self.bounds.size, nil); if(!tempLayer) tempLayer = CGLayerCreateWithContext(ctx, self.bounds.size, nil); // Draw the persistant drawing CGContextDrawLayerAtPoint(ctx, CGPointMake(0, 0), persistentLayer); // Overlay with the temporary drawing CGContextDrawLayerAtPoint(ctx, CGPointMake(0, 0), tempLayer); } - (void)commitDrawing { // Persist the temporary drawing CGContextRef ctx = CGLayerGetContext(persistentLayer); CGContextDrawLayerAtPoint(ctx, CGPointMake(0, 0), tempLayer); [self discardDrawing]; } - (void)discardDrawing { // Clears the temporary layer CGContextRef ctx = CGLayerGetContext(tempLayer); CGContextClearRect(ctx, self.bounds); CGContextFlush(ctx); } @end 

EDIT: agregué la lógica de que si se detecta un nuevo toque, si actualmente hay un trazo dibujado con un mayor valor de y, se elimina, como lo comentamos en los comentarios.

La superposition se realiza pintando dos CGLayer s. Este código podría optimizarse mucho para el performance, debería considerarse más como una ilustración que como un código preparado para la producción.

Use un UIPanGestureRecognizer para manejar los toques en su lugar, y establezca su propiedad maximumNumberOfTouches en 1, para que solo reconozca un toque a la vez.

El gestor de reconocimiento se encargará de ignorar las cosas que son solo grifos, porque es específicamente para reconocer las cacerolas. Además, al establecer el número máximo de toques en 1, una vez que comiencen a escribir, ningún otro toque tendrá ningún efecto en él, seguirá automáticamente rastreando solo el primer toque.

EDITAR:

Aquí hay un ejemplo simple, que comienza con una plantilla de aplicación de vista única básica y reemplaza todo lo que está debajo de la instrucción #import con lo siguiente:

 @interface ViewController () @property (nonatomic, strong) UIPanGestureRecognizer *panGestureRecognizer; @property (nonatomic, strong) UIImageView *imageView; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; self.imageView = [[UIImageView alloc] initWithFrame:self.view.bounds]; [self.view addSubview:self.imageView]; self.panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(performPanGesture:)]; self.panGestureRecognizer.maximumNumberOfTouches = 1; [self.view addGestureRecognizer:self.panGestureRecognizer]; } - (void)viewDidLayoutSubviews { [super viewDidLayoutSubviews]; self.imageView.frame = self.view.bounds; } - (void)performPanGesture:(UIPanGestureRecognizer *)panGesture { if (panGesture == self.panGestureRecognizer) { CGPoint touchLocation = [panGesture locationInView:self.view]; // NSLog(@"%f, %f", touchLocation.x, touchLocation.y); UIGraphicsBeginImageContextWithOptions(self.view.frame.size, YES, 0.0); CGContextRef context = UIGraphicsGetCurrentContext(); [self.view.layer renderInContext:context]; CGContextAddEllipseInRect(context, CGRectMake(touchLocation.x - 1, touchLocation.y - 1, 3, 3)); CGContextDrawPath(context, kCGPathFill); UIImage *outputImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); self.imageView.image = outputImage; } } @end 

Esto dibujará pequeños círculos en la pantalla donde sea que se reciba un toque. También puede modificarlo para realizar un seguimiento del último punto de contacto y dibujar líneas entre los puntos para que tenga algo que sea continuo. También puede ver que tocar con un segundo dedo no comenzará a hacer puntos en un nuevo lugar, ya que solo maneja un toque y simplemente se ignora un segundo toque.

Cuando los toques comienzan, toca el set y mantén una reference al mismo. Cómo decides cuál tomar depende de ti, es de esperar que solo haya uno, o puedes verificar la location de cada toque y elegir el "más alto" en la pantalla.

A medida que los toques se movieron, compruebe que su toque almacenado sigue siendo válido (contenido en touches ) y, si es así, úselo solo (puede ignorar todos los demás toques).


En su forma más trivial:

 self.trackingTouch = [touches anyObject]; 

pero hay otras (mejores) forms de elegir qué toque almacenar.


No estoy realmente seguro de por qué los documentos dicen You should never retain an UITouch object when handling an event cuando también dicen que A UITouch object is persistent throughout a multi-touch sequence .

No he visto problemas con el almacenamiento del toque anteriormente, pero eso no significa que no pueda causar problemas en el futuro (de alguna manera). La alternativa basada en A UITouch object is persistent throughout a multi-touch sequence es para almacenar solo el puntero al tacto y usarlo solo para la comparación de pointers (por lo que no se UITouch ningún método en el object UITouch detrás del puntero).