UIGestureRecognizer 如何识别被点击的圆圈?

Posted

技术标签:

【中文标题】UIGestureRecognizer 如何识别被点击的圆圈?【英文标题】:How will the UIGestureRecognizer identify which circle was tapped? 【发布时间】:2016-02-05 10:49:15 【问题描述】:

我正在尝试点击五个圆圈中的一个并启动一个动画,该动画将逐渐将所选背景更改为与点击的圆圈相同的颜色。我已经设法让 UITapGestureRecognizer 响应五个圆圈中的任何一个上的点击手势,但我不知道如何找出如何识别每个圆圈。

UIGestureRecognizer 上的 UIKit 框架参考文档说

手势识别器对针对特定视图进行命中测试的触摸进行操作 以及该视图的所有子视图

还有那个

手势识别器的客户端也可以询问 通过调用 locationInView: 或 locationOfTouch:inView 来做手势。

这让我觉得可能需要将点击的圆圈制作成图像。

但这真的是我需要做的吗?

这是目前的代码

#import "ViewController.h"
@interface ViewController ()
@end

@implementation ViewController

- (void)viewDidLoad 
[super viewDidLoad];

self.view.backgroundColor = [UIColor colorWithRed:240/255.0 green:240/255.0 blue:240/255.0 alpha:1.0];

[self.view setUserInteractionEnabled:YES];

UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(method:)];
tap.numberOfTapsRequired = 1;
[self.view addGestureRecognizer:tap];    

CAShapeLayer *circleLayer1 = [CAShapeLayer layer];
[circleLayer1 setPath:[[UIBezierPath bezierPathWithOvalInRect:CGRectMake(75.0,  260.0, 50.0, 50.0)] CGPath]];
[circleLayer1 setStrokeColor:[[UIColor cyanColor] CGColor]];
[circleLayer1 setFillColor:[[UIColor cyanColor] CGColor]];
[[self.view layer] addSublayer:circleLayer1];    

CAShapeLayer *circleLayer2 = [CAShapeLayer layer];
[circleLayer2 setPath:[[UIBezierPath bezierPathWithOvalInRect:CGRectMake(125.0, 260.0, 50.0, 50.0)] CGPath]];
[circleLayer2 setStrokeColor:[[UIColor redColor] CGColor]];
[circleLayer2 setFillColor:[[UIColor redColor] CGColor]];
[[self.view layer] addSublayer:circleLayer2];    

CAShapeLayer *circleLayer3 = [CAShapeLayer layer];
[circleLayer3 setPath:[[UIBezierPath bezierPathWithOvalInRect:CGRectMake(175.0, 260.0, 50.0, 50.0)] CGPath]];
[circleLayer3 setStrokeColor:[[UIColor yellowColor] CGColor]];
[circleLayer3 setFillColor:[[UIColor yellowColor] CGColor]];
[[self.view layer] addSublayer:circleLayer3];
CAShapeLayer *circleLayer4 = [CAShapeLayer layer];

[circleLayer4 setPath:[[UIBezierPath bezierPathWithOvalInRect:CGRectMake(225.0, 260.0, 50.0, 50.0)] CGPath]];
[circleLayer4 setStrokeColor:[[UIColor magentaColor] CGColor]];
[circleLayer4 setFillColor:[[UIColor magentaColor] CGColor]];
[[self.view layer] addSublayer:circleLayer4];

CAShapeLayer *circleLayer5 = [CAShapeLayer layer];
[circleLayer5 setPath:[[UIBezierPath bezierPathWithOvalInRect:CGRectMake(275.0, 260.0, 50.0, 50.0)] CGPath]];
[circleLayer5 setStrokeColor:[[UIColor greenColor] CGColor]];
[circleLayer5 setFillColor:[[UIColor greenColor] CGColor]];
[[self.view layer] addSublayer:circleLayer5];


- (void)method:(id)sender 
[UIView animateWithDuration:3.0 animations:^
self.view.layer.backgroundColor = [UIColor cyanColor].CGColor;
 completion:NULL];

为了显示我正在尝试做的事情,我设置了方法,以便将背景层更改为左侧圆圈的颜色。

但是我需要做什么 [a] 来确定哪个圆圈被点击, [b] 将点击的点表示为圆圈,这些圆圈也会逐渐改变颜色,因此整个屏幕会变为被点击的圆圈的颜色?

【问题讨论】:

【参考方案1】:

我建议您删除添加到整个视图的 UITapGestureRecognizer。 然后为每个 CAShapeLayer 添加一个名称,以便您可以区分它们:

self.view.backgroundColor = [UIColor colorWithRed:240/255.0 green:240/255.0 blue:240/255.0 alpha:1.0];

[self.view setUserInteractionEnabled:YES];

CAShapeLayer *circleLayer1 = [CAShapeLayer layer];
[circleLayer1 setPath:[[UIBezierPath bezierPathWithOvalInRect:CGRectMake(75.0,  260.0, 50.0, 50.0)] CGPath]];
[circleLayer1 setStrokeColor:[[UIColor cyanColor] CGColor]];
[circleLayer1 setFillColor:[[UIColor cyanColor] CGColor]];
[circleLayer1 setName:@"circleLayer1"];
[[self.view layer] addSublayer:circleLayer1];

CAShapeLayer *circleLayer2 = [CAShapeLayer layer];
[circleLayer2 setPath:[[UIBezierPath bezierPathWithOvalInRect:CGRectMake(125.0, 260.0, 50.0, 50.0)] CGPath]];
[circleLayer2 setStrokeColor:[[UIColor redColor] CGColor]];
[circleLayer2 setFillColor:[[UIColor redColor] CGColor]];
[circleLayer2 setName:@"circleLayer2"];
[[self.view layer] addSublayer:circleLayer2];

CAShapeLayer *circleLayer3 = [CAShapeLayer layer];
[circleLayer3 setPath:[[UIBezierPath bezierPathWithOvalInRect:CGRectMake(175.0, 260.0, 50.0, 50.0)] CGPath]];
[circleLayer3 setStrokeColor:[[UIColor yellowColor] CGColor]];
[circleLayer3 setFillColor:[[UIColor yellowColor] CGColor]];
 [circleLayer3 setName:@"circleLayer3"];
[[self.view layer] addSublayer:circleLayer3];
CAShapeLayer *circleLayer4 = [CAShapeLayer layer];

[circleLayer4 setPath:[[UIBezierPath bezierPathWithOvalInRect:CGRectMake(225.0, 260.0, 50.0, 50.0)] CGPath]];
[circleLayer4 setStrokeColor:[[UIColor magentaColor] CGColor]];
[circleLayer4 setFillColor:[[UIColor magentaColor] CGColor]];
 [circleLayer4 setName:@"circleLayer4"];
[[self.view layer] addSublayer:circleLayer4];

CAShapeLayer *circleLayer5 = [CAShapeLayer layer];
[circleLayer5 setPath:[[UIBezierPath bezierPathWithOvalInRect:CGRectMake(275.0, 260.0, 50.0, 50.0)] CGPath]];
[circleLayer5 setStrokeColor:[[UIColor greenColor] CGColor]];
[circleLayer5 setFillColor:[[UIColor greenColor] CGColor]];
[circleLayer5 setName:@"circleLayer5"];
[[self.view layer] addSublayer:circleLayer5];

然后你可以添加这个方法来检测 CAShapeLayer 是否被触摸:

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event

    for (UITouch *touch in touches) 
        CGPoint touchLocation = [touch locationInView:self.view];
        for (id sublayer in self.view.layer.sublayers) 
            if ([sublayer isKindOfClass:[CAShapeLayer class]]) 
                CAShapeLayer *shapeLayer = sublayer;
                if (CGPathContainsPoint(shapeLayer.path, 0, touchLocation, YES)) 
                NSLog(@"Layer's name is: %@",shapeLayer.name);
                
            
        
    

现在您可以检测到哪个 CAShapeLayer 被触摸了,您可以根据自己的喜好自定义视图的颜色

【讨论】:

谢谢。我认为你的建议是我需要首先尝试的。它甚至解决了我原来的问题中没有提到的其他设计问题。如果事实证明是这样,我将编辑原始问题并包含新代码。然而,这不会马上发生,因为我还是 Objective C 的新手。 我是想通知你的。【参考方案2】:

有一个名为locationInView:(UIView *)view 的方法可以帮助您找到位置。您知道所有圆的矩形,并使用CGRectContainsPoint(CGRect rect, CGPoint point) 方法检查该位置是否在任何矩形内,如果在内部,您可以检查所选位置是否与centre 的距离为radius。希望这能解决您的问题。

对于Q.(b)我附上了一个样本,你可以参考一下,

UIColor *stroke = rippleColor ? rippleColor : [UIColor colorWithWhite:0.8 alpha:0.8];

    CGRect pathFrame = CGRectMake(-CGRectGetMidX(self.bounds), -CGRectGetMidY(self.bounds), self.bounds.size.width, self.bounds.size.height);
    UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:pathFrame cornerRadius:self.layer.cornerRadius];

    // accounts for left/right offset and contentOffset of scroll view
    CGPoint shapePosition = [self convertPoint:self.center fromView:nil];

    CAShapeLayer *circleShape = [CAShapeLayer layer];
    circleShape.path = path.CGPath;
    circleShape.position = shapePosition;
    circleShape.fillColor = [UIColor clearColor].CGColor;
    circleShape.opacity = 0;
    circleShape.strokeColor = stroke.CGColor;
    circleShape.lineWidth = 3;

    [self.layer addSublayer:circleShape];


    [CATransaction begin];
    //remove layer after animation completed
    [CATransaction setCompletionBlock:^
        [circleShape removeFromSuperlayer];
    ];

    CABasicAnimation *scaleAnimation = [CABasicAnimation animationWithKeyPath:@"transform.scale"];
    scaleAnimation.fromValue = [NSValue valueWithCATransform3D:CATransform3DIdentity];
    scaleAnimation.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(2.5, 2.5, 1)];

    CABasicAnimation *alphaAnimation = [CABasicAnimation animationWithKeyPath:@"opacity"];
    alphaAnimation.fromValue = @1;
    alphaAnimation.toValue = @0;

    CAAnimationGroup *animation = [CAAnimationGroup animation];
    animation.animations = @[scaleAnimation, alphaAnimation];
    animation.duration = 0.5f;
    animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
    [circleShape addAnimation:animation forKey:nil];

    [CATransaction commit];


[UIView animateWithDuration:0.1 animations:^
    imageView.alpha = 0.4;
    self.layer.borderColor = [UIColor colorWithWhite:1 alpha:0.9].CGColor;
completion:^(BOOL finished) 
    [UIView animateWithDuration:0.2 animations:^
        imageView.alpha = 1;
        self.layer.borderColor = [UIColor colorWithWhite:0.8 alpha:0.9].CGColor;
    completion:^(BOOL finished) 
        if([superSender respondsToSelector:methodName])
            [superSender performSelectorOnMainThread:methodName withObject:nil waitUntilDone:NO];
        

        if(_block) 
            BOOL success= YES;
            _block(success);
        
    ];

];

`

【讨论】:

@Rafeek,谢谢,这证实了我的预感,即不需要将圆圈制作成图像,并明确表示它在方法中:我需要检查点击的点是否在圆的矩形边界。您还为我提供了 [b] 的一些动画选项,我想在查看 metronic 建议的方法后尝试一下。【参考方案3】:

正如其他答案所指出的,您有一些选择,但我认为最简单的方法是检查您的方法,您点击了哪一层。

你只需要把你的方法改成这样:

- (void)method:(UITapGestureRecognizer *)gesture 
    CGPoint touchLocation = [gesture locationInView:self.view];
    for (id sublayer in self.view.layer.sublayers) 
        if ([sublayer isKindOfClass:[CAShapeLayer class]]) 
            CAShapeLayer *shapeLayer = sublayer;
            if (CGPathContainsPoint(shapeLayer.path, 0, touchLocation, YES)) 
                [UIView animateWithDuration:3.0 animations:^
                    shapeLayer.strokeColor = [UIColor blackColor].CGColor;
                    shapeLayer.fillColor = [UIColor blackColor].CGColor;
                 completion:NULL];
            
        
    


【讨论】:

@ggrana,谢谢。检查方法内部以查看哪个层被点击肯定似乎是要走的路。我会检查你的 UITapGestureRecognizer 方法,看看它比 metronic 提出的 Touches 方法有什么优势。对我来说,决定性因素将是 [a] 该方法是否支持除点击以外的手势,更重要的是 [b] 在 android 平台上实现应用程序时,哪种解决方案更有可能更容易。跨度> 【参考方案4】:

你不需要在你的视图中添加任何UITapGestureRecognizer,你只需要为你的层添加名称并实现以下方法:

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
    for (UITouch *touch in touches) 
        CGPoint touchLocation = [touch locationInView:self.view];
        for (id sublayer in self.view.layer.sublayers) 
            if ([sublayer isKindOfClass:[CAShapeLayer class]]) 
                CAShapeLayer *shapeLayer = sublayer;
                if (CGPathContainsPoint(shapeLayer.path, 0, touchLocation, YES)) 
                    // This touch is in this shape layer
                    NSLog(@"Name of layer is: %@",shapeLayer.name);   
                
             else 
                CALayer *layer = sublayer;
                if (CGRectContainsPoint(layer.frame, touchLocation)) 
                    // Touch is in this rectangular layer
                
            
        
    

识别图层后,您可以相应地更改图层颜色。

或者,如果您的层数有限,您可以将其识别为以下层数:

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
        for (UITouch *touch in touches) 
            CGPoint touchLocation = [touch locationInView:self.view];
            if (CGPathContainsPoint(shape1.path, 0, touchLocation, YES)) 
                // This touch is in this shape layer
                NSLog(@"Name of layer 1 is: %@",shape1.name);
            
            if (CGPathContainsPoint(shape2.path, 0, touchLocation, YES)) 
                // This touch is in this shape layer
                NSLog(@"Name of layer 2 is: %@",shape2.name);
            
            if (CGPathContainsPoint(shape3.path, 0, touchLocation, YES)) 
                // This touch is in this shape layer
                NSLog(@"Name of layer 3 is: %@",shape3.name);
            
            if (CGPathContainsPoint(shape4.path, 0, touchLocation, YES)) 
                    // This touch is in this shape layer
                    NSLog(@"Name of layer 4 is: %@",shape4.name);
                
            if (CGPathContainsPoint(shape5.path, 0, touchLocation, YES)) 
                    // This touch is in this shape layer
                    NSLog(@"Name of layer 5 is: %@",shape5.name);
                
        
    

【讨论】:

谢谢。当我跟进 metronic 建议的方法时,这仍然可能是解决方案的一部分。我最初的问题涉及有限的层,即 5 层中的 1 层。下一个状态处理 16 层中的 1 层,但我不认为这会是一个问题。在我尝试第一个方法之前,我总是可以从你的方法的第二个版本开始。

以上是关于UIGestureRecognizer 如何识别被点击的圆圈?的主要内容,如果未能解决你的问题,请参考以下文章

如果多个带有手势的视图重叠,如何将 UIGestureRecognizer 放在前面

关于iOS的手势UIGestureRecognizer问题

手势识别UIGestureRecognizer

`UIGestureRecognizer`命中测试

UIGestureRecognizer 在屏幕外渲染

UIGestureRecognizer