How do I detect a touch event on a moving UIImageView? - cocoa-touch

When researching "How do I detect a touch event on a moving UIImageView?" I've come across several answers and I tried to implement them to my app. Nothing I've come across seems to work. I'll explain what I'm trying to do then post my code. Any thoughts, suggestions, comments or answers are appreciated!
My app has several cards floating across the screen from left to right. These cards are various colors and the object of the game is the drag the cards down to their similarly colored corresponding container. If the user doesn't touch and drag the cards fast enough, the cards will simply drift off the screen and points will be lost. The more cards contained in the correct containers, the better the score.
I've written code using core animation to have my cards float from the left to right. This works. However when attempting to touch a card and drag it toward it's container, it isn't correctly detecting that I'm touching the UIImageView of the card.
To test if my I'm properly implementing the code to move a card, I've also written some code allows movement for a non-moving card. In this case my touch is being detected and acting accordingly.
Why can I only interact with stationary cards? After researching this quite a bit it seems that the code:
options:UIViewAnimationOptionAllowUserInteraction
is the key ingredient to get my moving UIImages to be detected. However I tried this doesn't seem to have any effect.
I another key thing that I may be doing wrong is not properly utilizing the correct presentation layer. I've added code like this to my project and I also only works on non-moving objects:
UITouch *t = [touches anyObject];
UIView *myTouchedView = [t view];
CGPoint thePoint = [t locationInView:self.view];
if([_card.layer.presentationLayer hitTest:thePoint])
{
NSLog(#"You touched a Card!");
}
else{
NSLog(#"backgound touched");
}
After trying these types of things I'm getting stuck. Here is my code to understand this a bit more completely:
#import "RBViewController.h"
#import <QuartzCore/QuartzCore.h>
#interface RBViewController ()
#property (nonatomic, strong) UIImageView *card;
#end
#implementation RBViewController
- (void)viewDidLoad
{
srand(time (NULL)); // will be used for random colors, drift speeds, and locations of cards
[super viewDidLoad];
[self setOutFirstCardSet]; // this sends out 4 floating cards across the screen
// the following creates a non-moving image that I can move.
_card = [[UIImageView alloc] initWithFrame:CGRectMake(400,400,100,100)];
_card.image = [UIImage imageNamed:#"goodguyPINK.png"];
_card.userInteractionEnabled = YES;
[self.view addSubview:_card];
}
the following method sends out cards from a random location on the left side of the screen and uses core animation to drift the card across the screen. Notice the color of the card and the speed of the drift will be randomly generated as well.
-(void) setOutFirstCardSet
{
for(int i=1; i < 5; i++) // sends out 4 shapes
{
CGRect cardFramei;
int startingLocation = rand() % 325;
CGRect cardOrigini = CGRectMake(-100,startingLocation + 37, 92, 87);
cardFramei.size = CGSizeMake(92, 87);
CGPoint origini;
origini.y = startingLocation + 37;
origini.x = 1200;
cardFramei.origin = origini;
_card.userInteractionEnabled = YES;
_card = [[UIImageView alloc] initWithFrame:cardOrigini];
int randomColor = rand() % 7;
if(randomColor == 0)
{
_card.image = [UIImage imageNamed:#"goodguy.png"];
}
else if (randomColor == 1)
{
_card.image = [UIImage imageNamed:#"goodguyPINK.png"];
}
else if (randomColor == 2)
{
_card.image = [UIImage imageNamed:#"goodGuyPURPLE.png"];
}
else if (randomColor == 3)
{
_card.image = [UIImage imageNamed:#"goodGuyORANGE.png"];
}
else if (randomColor == 4)
{
_card.image = [UIImage imageNamed:#"goodGuyLightPINK.png"];
}
else if (randomColor == 5)
{
_card.image = [UIImage imageNamed:#"goodGuyBLUE.png"];
}
else if (randomColor == 6)
{
_card.image = [UIImage imageNamed:#"goodGuyGREEN.png"];
}
_card.userInteractionEnabled = YES; // this is also written in my viewDidLoad method
[[_card.layer presentationLayer] hitTest:origini]; // not really sure what this does
[self.view addSubview:_card];
int randomSpeed = rand() % 20;
int randomDelay = rand() % 2;
[UIView animateWithDuration:randomSpeed + 10
delay: randomDelay + 4
options:UIViewAnimationOptionAllowUserInteraction // here is the method that I thought would allow me to interact with the moving cards. Not sure why I can't
animations: ^{
_card.frame = cardFramei;
}
completion:NULL];
}
}
notice the following method is where I put CALayer and hit test information. I'm not sure if I'm doing this correctly.
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *t = [touches anyObject];
UIView *myTouchedView = [t view];
CGPoint thePoint = [t locationInView:self.view];
thePoint = [self.view.layer convertPoint:thePoint toLayer:self.view.layer.superlayer];
CALayer *theLayer = [self.view.layer hitTest:thePoint];
if([_card.layer.presentationLayer hitTest:thePoint])
{
NSLog(#"You touched a Shape!"); // This only logs when I touch a non-moving shape
}
else{
NSLog(#"backgound touched"); // this logs when I touch the background or an moving shape.
}
if(myTouchedView == _card)
{
NSLog(#"Touched a card");
_boolHasCard = YES;
}
else
{
NSLog(#"Didn't touch a card");
_boolHasCard = NO;
}
}
I want the following method to work on moving shapes. It only works on non-moving shapes. Many answers say to have the touch ask which class the card is from. As of now all my cards on of the same class (the viewController class). When trying to have the cards be their own class, I was having trouble having that view appear on my main background controller. Must I have various cards be from different classes for this to work, or can I have it work without needing to do so?
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [[event allTouches] anyObject];
if([touch view]==self.card)
{
CGPoint location = [touch locationInView:self.view];
self.card.center=location;
}
}
This next method resets the movement of a card if the user starts moving it and then lifts up on it.
-(void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
if(_boolHasCard == YES)
{
[UIView animateWithDuration:3
delay: 0
options:UIViewAnimationOptionAllowUserInteraction
animations: ^{
CGRect newCardOrigin = CGRectMake(1200,_card.center.y - 92/2, 92, 87);
_card.frame = newCardOrigin;
}
completion:NULL];
}
}
#end

The short answer is, you can't.
Core Animation does not actually move the objects along the animation path. They move the presentation layer of the object's layer.
The moment the animation begins, the system thinks the object is at it's destination.
There is no way around this if you want to use Core Animation.
You have a couple of choices.
You can set up a CADisplayLink on your view controller and roll your own animation, where you move the center of your views by a small amount on each call to the display link. This might lead to poor performance and jerky animation if you're animating a lot of objects however.
You can add a gesture recognizer to the parent view that contains all your animations, and then use layer hit testing on the paren't view's presentation view to figure out which animating layer got tapped, then fetch that layer's delegate, which will be the view you are animating. I have a project on github that shows how to do this second technique. It only detects taps on a single moving view, but it will show you the basics: Core Animation demo project on github.
(up-votes always appreciated if you find this post helpful)

It looks to me that your problem is really with just an incomplete understanding of how to convert a point between coordinate spaces. This code works exactly as expected:
- (void)viewDidLoad
{
[super viewDidLoad];
CGPoint endPoint = CGPointMake([[self view] bounds].size.width,
[[self view] bounds].size.height);
CABasicAnimation *animation = [CABasicAnimation
animationWithKeyPath:#"position"];
animation.fromValue = [NSValue valueWithCGPoint:[[_imageView layer] position]];
animation.toValue = [NSValue valueWithCGPoint:endPoint];
animation.duration = 30.0f;
[[_imageView layer] addAnimation:animation forKey:#"position"];
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *t = [touches anyObject];
CGPoint thePoint = [t locationInView:self.view];
thePoint = [[_imageView layer] convertPoint:thePoint
toLayer:[[self view] layer]];
if([[_imageView layer].presentationLayer hitTest:thePoint])
{
NSLog(#"You touched a Shape!");
}
else{
NSLog(#"backgound touched");
}
}
Notice the line in particular:
thePoint = [[_imageView layer] convertPoint:thePoint
toLayer:[[self view] layer]];
When I tap on the layer image view while it's animating, I get "You touched a Shape!" in the console window and I get "background touched" when I tap around it. That's what you're wanting right?
Here's a sample project on Github
UPDATE
To help with your follow up question in the comments, I've written the touchesBegan code a little differently. Imagine that you've add all of your image views to an array (cleverly named imageViews) when you create them. You would alter your code to look something like this:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *t = [touches anyObject];
CGPoint thePoint = [t locationInView:self.view];
for (UIImageView *imageView in [self imageViews]) {
thePoint = [[imageView layer] convertPoint:thePoint
toLayer:[[self view] layer]];
if([[imageView layer].presentationLayer hitTest:thePoint]) {
NSLog(#"Found it!!");
break; // No need to keep iterating, we've found it
} else{
NSLog(#"Not this one!");
}
}
}
I'm not sure how expensive this is, so you may have to profile it, but it should do what you're expecting.

Related

Delayed animation when a pan gesture leaves a UIView, with debouncing?

Are there one or two idiomatic ways to handle the kind of UI interaction described below? Perhaps a custom class is unnecessary?
I am implementing drag and drop in an iPadd app and would like to handle the case where the draggable is not released upon the droppable and the pan gesture leaves the UIView.
The view expands and gets a border when the draggable is over it, and it will return to its previous size and lose the border when the draggable leaves the area. There will be a noticeable delay before the down-scale animation begins.
It is possible the draggable will be brought back over the zone before the scale-down animation begins, suggesting the need for some kind of debouncing, i.e., something to collect the events that occur within a period of time and treat them as one event.
I know a large number of events are fired off during a pan gesture, and I don't want to allocate unnecessary resources (e.g., timers).
I was thinking of using a single custom timer, perhaps along these lines, but perhaps there's something much simpler for this?
Following code will inflate the view with inflate-animation of 300ms whenever a finger moves over the view and will deflate the view back to normal whenever the touch is outside. No panGestureRecognizerRequired.
#interface CustomView : UIView
{
BOOL hasExpanded;
CGRect initialFrame;
CGRect inflatedFrame;
}
#end
#implementation CustomView
-(id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if(self)
{
hasExpanded = NO;
initialFrame = frame;
CGFloat inflateIncrement = 50.0f;
inflatedFrame = CGRectMake(self.frame.origin.x-(inflateIncrement*0.5f),
self.frame.origin.y-(inflateIncrement*0.5f),
self.frame.size.width+inflateIncrement,
self.frame.size.height+inflateIncrement);
}
return self;
}
-(void)forceDeflate
{
if (hasExpanded)
{
//start deflating view animation
[UIView animateWithDuration:0.3 animations:^{
self.frame = initialFrame;
}];
hasExpanded = NO;
}
}
-(void)inflateByCheckingPoint:(CGPoint)touchPoint
{
if(!hasExpanded)
{
if(CGRectContainsPoint(self.frame,touchPoint))
{
//start inflating view animation
[UIView animateWithDuration:0.3 animations:^{
self.frame = inflatedFrame;
}];
hasExpanded = YES;
}
}
else
{
if(!CGRectContainsPoint(self.frame,touchPoint))
{
//start deflating view animation
[UIView animateWithDuration:0.3 animations:^{
self.frame = initialFrame;
}];
hasExpanded = NO;
}
}
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *singleTouch = [touches anyObject];
CGPoint touchPoint = [singleTouch locationInView:self.superview];
[self inflateByCheckingPoint:touchPoint];
}
-(void)touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event
{
UITouch *singleTouch = [touches anyObject];
CGPoint touchPoint = [singleTouch locationInView:self.superview];
[self inflateByCheckingPoint:touchPoint];
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
[self forceDeflate];
}
-(void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
[self forceDeflate];
}
#end

How to add images while moving finger across UIView?

I am having issues adding an image across a view while moving finger across the screen.
Currently it is adding the image multiple times but squeezing them too close together and not really following my touch.
EDIT:
What I want:
After taking or choosing an image, the user can then select another image from a list. I want the user to touch and move their finger across the view and the selected image will appear where they drag their finger, without overlapping, in each location.
This works:
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
currentTouch = [touch locationInView:self.view];
CGRect myImageRect = CGRectMake(currentTouch.x, currentTouch.y, 80.0f, 80.0f);
myImage = [[UIImageView alloc] initWithFrame:myImageRect];
[myImage setImage:[UIImage imageNamed:#"dot.png"]];
[self.view addSubview:myImage];
[myImage release];
}
New Question: How do I add spaces in this so that the image isn't so snug when drawing it on the view?
You might want to explain your question more, what exactly are you trying to achieve! If you dont want the images to overlap than you can try this!
UITouch * touch = [touches anyObject];
touchPoint = [touch locationInView:imageView];
prev_touchPoint = [touch previousLocationInView:imageView];
if (ABS(touchPoint.x - prev_touchPoint.x) > 80
|| ABS(touchPoint.y - prev_touchPoint.y) > 80) {
_aImageView = [[UIImageView alloc] initWithImage:aImage];
_aImageView.multipleTouchEnabled = YES;
_aImageView.userInteractionEnabled = YES;
[_aImageView setFrame:CGRectMake(touchPoint.x, touchPoint.y, 80.0, 80.0)];
[imageView addSubview:_aImageView];
[_aImageView release];
}
I'm sorry because I'm in company and I can't post too big data(the code). The squeezing is because you have not checked the touch point's distance with last touch point. Check a point whether it's in a view:bool CGRectContainsPoint (CGRect rect,CGPoint point);I mean remember a touch point in touchesBegan:. Update it if the new touches in touchesMomved: is bigger than image's width or left. And put the add image view in a method and call it use - (void)performSelectorInBackground:(SEL)aSelector withObject:(id)arg.
you can also use UISwipeGestureRecognizer as below instead of touchesMoved method,while swiping across the screen.in viewDidload:method,
UISwipeGestureRecognizer *swipeup = [[UISwipeGestureRecognizer alloc]initWithTarget:self action:#selector(swipedone:)];
swipeup.direction = UISwipeGestureRecognizerDirectionUp;
swipeup.numberOfTouchesRequired=1;
[self.view addGestureRecognizer:swipeup];
method definition:
-(IBAction)swipedone:(UISwipeGestureRecognizer*)recognizer
{
NSLog(#"swiped");
UIImageView* _aImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"1.png"]];
_aImageView.frame = CGRectMake(10, 10, 100, 100);
_aImageView.multipleTouchEnabled = YES;
_aImageView.userInteractionEnabled = YES;
CGPoint point = [recognizer locationInView:recognizer.view];
[_aImageView setFrame:CGRectMake(point.x, point.y, 80.0, 80.0)];
[self.view addSubview:_aImageView];
[_aImageView release];
}
currently i am using this code for swipe up.i think it will works fine.once try it.

CAEmitterLayer emits random unwanted particles on touch events

I'm trying to set up a CAEmitterLayer to make a confetti effect, and I've run into two issues:
Whenever I set the birthRate on my cells to something non-zero to start the animation I get a flurry of cells placed randomly on screen, which animate normally, and then the emitter continues to emit properly after that.
Whenever the emitterCells are drawing things on screen, any time I touch the screen, the emitter draws emitterCells in (seemingly) random locations that exist for a (seemingly) random amount of time. Nothing in the emitter is tied to any touch events (i.e. I'm not intentionally drawing anything on a touch event), but the layer is in a view that has multiple embedded views. The more I touch, the more cells show up.
Here's my code for setting up the emitter, and then starting and stopping it (once I've called the stop function, then taps on the screen cease creating new random elements):
- (void)setupConfetti
{
self.confettiLayer = [CAEmitterLayer layer];
[self.view.layer addSublayer:self.confettiLayer];
[self.view.layer setNeedsDisplay];
self.confettiLayer.emitterPosition = CGPointMake(1024.0/2,-50.0);
self.confettiLayer.emitterSize = CGSizeMake(1000.0, 10.0);
self.confettiLayer.emitterShape = kCAEmitterLayerLine;
self.confettiLayer.renderMode =kCAEmitterLayerUnordered;
CAEmitterCell *confetti = [CAEmitterCell emitterCell];
confetti1.contents = (id)[[UIImage imageNamed:#"confetti.png"] CGImage];
confetti.emissionLongitude = M_PI;
confetti.emissionLatitude = 0;
confetti.lifetime = 5;
confetti.birthRate = 0.0;
confetti.velocity = 125;
confetti.velocityRange = 50;
confetti.yAcceleration = 50;
confetti.spin = 0.0;
confetti.spinRange = 10;
confetti.name = #"confetti1";
self.confettiLayer.emitterCells = [NSArray arrayWithObjects:confetti, nil];
}
To start the confetti:
- (void)startConfettiAnimation
{
[self.confettiLayer setValue:[NSNumber numberWithInt:10.0] forKeyPath:#"emitterCells.confetti.birthRate"];
}
And to stop it:
- (void)stopConfettiAnimation
{
[self.confettiLayer setValue:[NSNumber numberWithInt:0.0] forKeyPath:#"emitterCells.confetti.birthRate"];
}
Again, once it gets started, after the initial flurry of random elements, this works just fine: everything animates normally, and when the birthRate is later set to zero, it ends gracefully. It just seems to respond to touch events, and I have no idea why. I've tried adding the emitterLayer to a different view, disabling user interaction on that view, and then adding it as a subview of the main view, and that didn't seem to work.
Any help/insight would be much appreciated!
Thanks,
Sam
I know this is an old post, but I also had this problem.
Jackslash answers it well in this post:
iOS 7 CAEmitterLayer spawning particles inappropriately
You need to set beginTime on your emitter layer to begin at the current time with CACurrentMediaTime(). It seems the problem we have occurs because the emitter started already in the past.
emitter.beginTime = CACurrentMediaTime();
Could it be that you aren't checking to see if the particle is emitting like in the Wenderlich example Artur Ozieranski posted? I'm not seeing the doubling as long as the check is in place.
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[fireView setEmitterPositionFromTouch: [touches anyObject]];
[fireView setIsEmitting:YES];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
[fireView setIsEmitting:NO];
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
[fireView setIsEmitting:NO];
}
-(void)setIsEmitting:(BOOL)isEmitting
{
//turn on/off the emitting of particles
[fireEmitter setValue:[NSNumber numberWithInt:isEmitting?200:0] forKeyPath:#"emitterCells.fire.birthRate"];
}
.h file
#import <UIKit/UIKit.h>
#interface DWFParticleView : UIView
-(void)setEmitterPositionFromTouch: (CGPoint*)t;
-(void)setIsEmitting:(BOOL)isEmitting;
#end
.m file
#import "DWFParticleView.h"
#import <QuartzCore/QuartzCore.h>
#implementation DWFParticleView
{
CAEmitterLayer* fireEmitter; //1
}
-(void)awakeFromNib
{
//set ref to the layer
fireEmitter = (CAEmitterLayer*)self.layer; //2
//configure the emitter layer
fireEmitter.emitterPosition = CGPointMake(50, 50);
fireEmitter.emitterSize = CGSizeMake(10, 10);
CAEmitterCell* fire = [CAEmitterCell emitterCell];
fire.birthRate = 0;
fire.lifetime = 1.5;
fire.lifetimeRange = 0.3;
fire.color = [[UIColor colorWithRed:255 green:255 blue:255 alpha:0.1] CGColor];
fire.contents = (id)[[UIImage imageNamed:#"Particles_fire.png"] CGImage];
[fire setName:#"fire"];
fire.velocity =5;
fire.velocityRange = 20;
fire.emissionRange = M_PI_2;
fire.scaleSpeed = 0.1;
fire.spin = 0.5;
fireEmitter.renderMode = kCAEmitterLayerAdditive;
//add the cell to the layer and we're done
fireEmitter.emitterCells = [NSArray arrayWithObject:fire];
}
+ (Class) layerClass //3
{
//configure the UIView to have emitter layer
return [CAEmitterLayer class];
}
-(void)setEmitterPositionFromTouch: (CGPoint*)t
{
//change the emitter's position
fireEmitter.emitterPosition = (*t);
}
-(void)setIsEmitting:(BOOL)isEmitting
{
//turn on/off the emitting of particles
[fireEmitter setValue:[NSNumber numberWithInt:isEmitting?100:0] forKeyPath:#"emitterCells.fire.birthRate"];
}
#end
I used this code for create a custom view and to emit particles on
touch
Here is the call statement for emission of particle on touch
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
CGPoint p = [[touches anyObject] locationInView:self.view];
[fireView setEmitterPositionFromTouch: &p];
[fireView setIsEmitting:YES];
}
may be it will work for you .

If statement to choose between multiple CALayer

I have a Core Animation image on boxLayer and I'm duplicating it, changing the action of and shifting the position of the 2nd (boxLayer2) so that someone can choose between the 2.
I want the user to be able to tap the image for boxLayer and the boxLayer2 image does nothing but boxLayer moves (I didn't include my animation code beyond receiving the touch) and viceversa.
I cannot get an if statement to work. I've tried multiple variations self.layer == boxLayer or CALayer == boxlayer ... sublayer is an array so that's out. Any help/explanation as I know I'm missing something would be greatly appreciated.
Thanks!
UIView *BounceView is declared in the VC
In BounceView I have 2 CALayers declared: boxlayer & boxlayer2
BounceView.m
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self setBackgroundColor:[UIColor clearColor]];
// Create the new layer object
boxLayer = [[CALayer alloc] init];
boxLayer2 = [[CALayer alloc] init];
// Give it a size
[boxLayer setBounds:CGRectMake(0.0, 0.0, 185.0, 85.0)];
[boxLayer2 setBounds:CGRectMake(0.0, 0.0, 185.0, 85.0)];
// Give it a location
[boxLayer setPosition:CGPointMake(150.0, 140.0)];
[boxLayer2 setPosition:CGPointMake(150.0, 540.0)];
// Create a UIImage
UIImage *layerImage = [UIImage imageNamed:#"error-label.png"];
UIImage *layerImage2 = [UIImage imageNamed:#"error-label.png"];
// Get the underlying CGImage
CGImageRef image = [layerImage CGImage];
CGImageRef image2 = [layerImage2 CGImage];
// Put the CGImage on the layer
[boxLayer setContents:(__bridge id)image];
[boxLayer2 setContents:(__bridge id)image2];
// Let the image resize (without changing the aspect ratio)
// to fill the contentRect
[boxLayer setContentsGravity:kCAGravityResizeAspect];
[boxLayer2 setContentsGravity:kCAGravityResizeAspect];
// Make it a sublayer of the view's layer
[[self layer] addSublayer:boxLayer];
[[self layer] addSublayer:boxLayer2];
}
return self;
}
- (void)touchesBegan:(NSSet *)touches
withEvent:(UIEvent *)event
{
if (CAlayer == boxLayer)
{
// do something
}
else
{
// do something else
}
}
It looks to me like you are trying to know what layer the user tapped on inside touched began and that this is your problem.
How to find out what layer was tapped
CALayer has an instance method - (CALayer *)hitTest:(CGPoint)thePoint that
Returns the farthest descendant of the receiver in the layer hierarchy (including itself) that contains a specified point.
So to find out what layer you tapped you should do something like
- (void)touchesBegan:(NSSet *)touches
withEvent:(UIEvent *)event {
UITouch *anyTouch = [[event allTouches] anyObject];
CGPoint pointInView = [anyTouch locationInView:self];
// Now you can test what layer that was tapped ...
if ([boxLayer hitTest:pointInView]) {
// do something with boxLayer
}
// the rest of your code
}
This works because hitTest will return nil if the point is outside the layer's bounds.
David Rönnqvist's post tells you how to use hitTest on the layer to figure out which layer was touched. That should work. I would code that method slightly differently, though. I would have my view's layer include boxLayer and boxLayer2 as sub-layers, and then send the hitTest method to the parent layer. It would then return the layer that contains the touch.
It would be much simpler, though, if you use separate views, each with a layer that contains your content. Then you can use gesture recognizers on each view, and higher level Cocoa Touch code rather than CA code to manage taps. Cleaner and easier to maintain.

Identify specific image view from multiple imageviews

I have a single image view, and I set other image views on it and put them in different locations. I want to apply FLIP ANIMATION on mage view. How can do it? when if(i==1) then my imageview is flipped. img1 is my image view and I want to identify it. I allocate 52 images in `imagearr
My code:
-(void)setCards
{
int k=0;
CGFloat dx = self.view.frame.size.width/2;
CGFloat dy = self.view.frame.size.height/2;
int cnt = [imagearr count];
for (int j=1; j<3; j++) // Number of cards
{
for (int i=1; i<5; i++) // Number of players
{
int rnd = (arc4random() % cnt)-1;
UIImage *img = [imagearr objectAtIndex:rnd];
UIImageView *img1 = [[UIImageView alloc] initWithImage:img];
CGRect frame = img1.frame;
frame.size.width = frameX;
frame.size.height = frameY;
[img1 setFrame:frame];
[img1 setTag:k+i];
[UIView beginAnimations:#"Move" context:nil];
[UIView setAnimationDuration:0.50];
if (i==1)
{
[img1 setCenter:CGPointMake(dx+100, dy+(j*15))];
}
if (i==2)
{
[img1 setCenter:CGPointMake(dx+(j*15), dy+60)];
}
if (i==3)
{
[img1 setCenter:CGPointMake(dx-130, dy-(j*15))];
}
if (i==4)
{
[img1 setCenter:CGPointMake(dx-(j*20), dy-95)];
}
[self.view addSubview:img1];
[UIView commitAnimations];
[img1 release];
}
k=k+10;
}
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [[event allTouches] anyObject];
int t = [touch view].tag;
}
I still can't identify a specific imageview. Please help me to solve this problem.
Setting tag for individual imageView is the best approach. Using tag you could even detect multiples images placed inside tableview cells and set action for selection of individual tag as well.
I think you should take a look at the Apple sample project GeekGameBoard. It implements a solitaire game and you will be able to learn how to select CALayers* representing cards, drag them around, and have them interact with each other.
*Which will be both easier and probably better from a performance perspective than using views.
Associate a unique tag values with each UIImageView instances.
Code for reference.
myImageView1.tag = 1;
myImageView1.tag = 2;
myImageView1.tag = 3;
Apple Link For the code:
Detect imageview from multiple imageview
Stack Link As Well
Here is the discussion for to detect the images from the multiple images
Good Luck
There will be different images in different imageView so each imageView will be on different position.
in your touch method do you have to check the bounds of the touch and then you have to match which imageView lies under your touch region.According to that you can proceed your functionality.
You can probably place a invisible UIbutton over your imageviews and capture touch event. The effect is the same and its much more cleaner and simpler. Let me know if you need more info. Would be happy to edit my answer here.
you could try something similar to this
-(void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event //here enable the touch
{
// get touch event
UITouch *touch = [[event allTouches] anyObject];
int t = [touch view].tag;
UIImageView *imageView = (UIImageView *) [self.view viewWithTag:t];
//your logic here
}