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 如何识别被点击的圆圈?的主要内容,如果未能解决你的问题,请参考以下文章