Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
199 views
in Technique[技术] by (71.8m points)

ios - How to ignore certain UITouch Points in multitouch sequence

I am working on a drawing app for ipad, I am providing full screen for drawing. So as we all now, user might write with his wrist support or by resting his hand on the screen. So my aim is to allow the user to write freely with his wrist/hand support.

But the app should only detect finger drawing, or ignore/reject wrist and hand touches and delete them

I started working on it , I created a sample project with multitouch enabled.

Below is my code

- (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(), red, 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];
    }
}

So this works fine with any number of touches, But I am not understanding how can I reject those palm/hand touches while drawing and only draw, what user draws with his finger/ stylus.

Presently if I draw, I get this thing, below is the image

Here I have drawn with my hand support, you can see below "Hello" their is few weird drawing has happened. How I can reject those touches and delete them and only draw hello

Thanks

Ranjit

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

One solution is to store the topmost tap in touchesBegan and only draw this one.

As you have pointed out, you are not supposed to retain the UITouch instance, so I recommend using a weak reference instead.

This will only draw a single touch. If you wish to draw the touches of multiple fingers, you need another way of filtering out the hand (many drawing apps have user settings for telling the app the pose of the hand, for example, but this is of course more complicated).

Here is an idea on how to do it:

#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 red=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, red, 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: I added the logic that if a new touch is detected, if there is currently any stroke being drawn with a higher y-value, it is removed, as we discussed in the comments.

The overlaying is done by painting two CGLayers. This code could be optimized a lot for performance, it should be looked at more as an illustration than production-ready code.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...