简体   繁体   中英

How will the UIGestureRecognizer identify which circle was tapped?

I am trying to tap one of five circles and start an animation that will gradually change the selected background to the same colour as the tapped circle. I've managed to get UITapGestureRecognizer to respond to a tap gesture on any one of five circles but I can't work out how to find out how to identify each circle.

The UIKit Framework Reference documentation on UIGestureRecognizer says

A gesture recognizer operates on touches hit-tested to a specific view and all of that view's subviews

and that

Clients of gesture recognisers can also ask for the location of a gesture by calling locationInView: or locationOfTouch:inView”.

This made me think the tapped circles probably need to be made into images.

But is this really what I need to do ?

Here's the code so far

#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];
}

To show what I'm trying to do, I've set up the method so it changes the background layer to the colour of the circle on the left.

But what do I need to do [a] to identify which circle was tapped and [b] represent the tapped spots as circles that would also gradually change colour so the whole screen changes to the colour of the circle that was tapped ?

I suggest you to remove the UITapGestureRecognizer that you added to the whole view. Then add a name to each CAShapeLayer so you can distinguish between them :

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];

Then you can add this method that allows you to detect wich CAShapeLayer has been touched:

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

Now that you can detect which CAShapeLayer has been touched, you can customize the color of your view as you prefer

There is a method called locationInView:(UIView *)view that will help you to find the location. You know the rects of all the circles and check whether the location is inside of any rect using the method CGRectContainsPoint(CGRect rect, CGPoint point) If its inside you can check whether the selected location is radius distance from the centre . Hope this will solve your issue.

For Q.(b) I attached a sample you can refer it,

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

}];

}`

As pointed by the others answers you have some options, but I think that the easiest, is to check in your method what layer you tapped.

You just need to change your method to something like that:

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

}

You don't need to add any UITapGestureRecognizer to your view, you just need to add names to your layers and implement the following method:

- (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
                }
            }
        }
    }
}

After identifing your layer you can change the color of layer accordingly.

Or if you have limited layers than you can identify it as follows for your no of layers:

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM