我正在为ipad绘制应用程序,我正在为绘图提供全屏。因此,就像我们现在一样,用户可能会用手腕支撑或将手放在屏幕上。所以我的目标是让用户用手腕/手支撑自由书写。
但应用程序应该只检测手指绘图,或忽略/拒绝手腕和手触摸并删除它们
我开始研究它,我创建了一个启用了多点触控的示例项目。
以下是我的代码
- (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];
}
}
所以这适用于任何数量的触摸,但我不明白如何在绘图时拒绝那些手掌/手部触摸,只绘制,用户用手指/手写笔绘制。
现在,如果我画画,我得到这个东西,下面是图像
在这里,我用我的手支持画了,你可以看到下面的“你好”,他们发现了一些奇怪的画。我如何拒绝这些接触并删除它们并且只绘制你好
由于
兰吉特
答案 0 :(得分:3)
一种解决方案是将最顶层的点按存储在touchesBegan
中,并仅绘制此点。
正如您所指出的,您不应该保留UITouch
实例,因此我建议使用弱引用。
这只会吸引一下。如果你想绘制多个手指的触摸,你需要另一种过滤手的方法(例如,许多绘图应用程序具有用于告诉应用程序手的姿势的用户设置,但这当然更复杂)。< / p>
以下是关于如何操作的想法:
#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
编辑:我添加了一个逻辑:如果检测到新的触摸,如果当前正在绘制任何具有更高y值的笔划,则会将其删除,正如我们在评论中所讨论的那样。
通过绘制两个CGLayer
来完成重叠。这段代码可以针对性能进行大量优化,应该将其视为一个插图,而不是生产就绪代码。
答案 1 :(得分:2)
使用UIPanGestureRecognizer
来处理触摸,并将其maximumNumberOfTouches
属性设置为1,这样它一次只能识别一次触摸。
手势识别器会处理忽略只是轻击的东西,因为它专门用于识别平底锅。此外,通过将最大触摸次数设置为1,一旦开始写入,其他任何触摸都不会对其产生任何影响,它将自动继续跟踪第一次触摸。
编辑:
这是一个简单的示例,从基本的单一视图应用程序模板开始,并使用以下内容替换#import语句下的所有内容:
@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
这会在接收到触摸的屏幕上绘制小圆圈。您还可以修改它以跟踪最后一个触摸点并在点之间绘制线条,这样您就可以获得连续的东西。您还可以看到用第二根手指触摸不会开始在新点上制作点,因为它只处理一次触摸,而第二次触摸则被忽略。
答案 2 :(得分:1)
当触摸开始时,从集合中触摸并保持对它的引用。你如何决定哪一个取决于你 - 希望只有一个,或者你可以检查每个触摸的位置并选择屏幕上的“最高”。
当触摸移动时,检查您存储的触摸是否仍然有效(包含在touches
中),如果是,则仅使用它(您可以忽略所有其他触摸)。
最重要的是:
self.trackingTouch = [touches anyObject];
但是还有其他(更好)的方法来选择存储哪种触摸。
当他们也说You should never retain an UITouch object when handling an event
时,不确定文档为什么会说A UITouch object is persistent throughout a multi-touch sequence
。
我之前没有看到存储触摸的问题,但这并不意味着它将来不会引起问题(某种程度上)。基于A UITouch object is persistent throughout a multi-touch sequence
的替代方法是仅存储指向触摸的指针并仅将其用于指针比较(因此在指针后面的UITouch
对象上不调用任何方法)。