如何在移动的UIImageView上检测到触摸事件?

时间:2013-03-30 19:54:31

标签: cocoa-touch uiimageview core-animation calayer uitouch

在研究“如何在移动的UIImageView上检测到触摸事件?”时我遇到了几个答案,我试图将它们应用到我的应用程序中。我遇到的任何东西似乎都没有用。我会解释我正在尝试做什么,然后发布我的代码。任何想法,建议,意见或答案都表示赞赏!

我的应用有几张卡片从左到右悬浮在屏幕上。这些卡是各种颜色的,游戏的目的是将卡拖到它们相似颜色的相应容器上。如果用户没有足够快地触摸并拖动卡片,则卡片将从屏幕上漂移并且点将丢失。正确容器中包含的卡越多,得分越高。

我使用核心动画编写代码让我的卡片从左向右浮动。这有效。但是,当试图触摸卡并将其拖向它的容器时,它无法正确检测到我正在触摸卡的UIImageView。

为了测试我是否正确实现移动卡的代码,我还写了一些代码允许移动非移动卡。在这种情况下,我的触摸被检测并相应地起作用。

为什么我只能与固定卡互动?经过相当多的研究后,似乎是代码:

 options:UIViewAnimationOptionAllowUserInteraction

是让我的移动UIImages被检测到的关键因素。但是我试过这似乎没有任何影响。

我可能做错的另一个关键因素是没有正确使用正确的表示层。我已将这样的代码添加到我的项目中,而且我也只处理非移动对象:

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

在尝试了这些类型的东西后,我陷入困境。这是我的代码,以更完整地理解这一点:

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

}

以下方法从屏幕左侧的随机位置发送卡片,并使用核心动画在屏幕上漂移卡片。请注意,卡片的颜色和漂移的速度也会随机生成。

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

请注意以下方法是我放置CALayer并点击测试信息的地方。我不确定我是否正确这样做。

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


}

我想要以下方法来处理移动形状。它仅适用于不移动的形状。很多答案都说触摸问卡是哪一类的。截至目前我所有的卡都在同一个类(viewController类)。当试图让这些卡成为他们自己的类时,我在主视频背景控制器上显示该视图时遇到了麻烦。为了实现这一目标,我是否必须拥有来自不同类别的各种卡片,或者我可以在不需要的情况下使用它吗?

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

如果用户开始移动卡片然后抬起卡片,则下一个方法会重置卡片的移动。

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

2 个答案:

答案 0 :(得分:2)

简短的回答是,你做不到。

Core Animation实际上并不沿着动画路径移动对象。他们移动对象图层的表示层。

动画开始的那一刻,系统认为对象就在它的目的地。

如果您想使用Core Animation,则无法解决此问题。

你有几个选择。

  1. 您可以在视图控制器上设置CADisplayLink并滚动自己的动画,在每次调用显示链接时,您可以将视图中心移动一小部分。如果您要为很多对象设置动画,这可能会导致性能不佳和动画不稳定。

  2. 您可以将手势识别器添加到包含所有动画的父视图中,然后在paren't视图的演示视图上使用图层命中测试来确定触摸了哪个动画层,然后获取该图层的委托,这将是你动画的视图。我在github上有一个项目,展示了如何做第二种技术。它仅检测单个移动视图上的点按,但它会显示基本信息:Core Animation demo project on github

  3. (如果你发现这篇文章很有帮助,那么总是赞赏)

答案 1 :(得分:1)

在我看来,你的问题实际上只是对如何在坐标空间之间转换点的不完全理解。此代码完全按预期工作:

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

}

特别注意这一行:

  thePoint = [[_imageView layer] convertPoint:thePoint 
                                      toLayer:[[self view] layer]];

当我在设置动画时点击图层图像视图时,我会看到“你碰到了一个形状!”在控制台窗口中,当我点击它时,我得到“背景触摸”。这就是你想要的吗?

这是sample project on Github

<强>更新

为了帮助您在评论中提出跟进问题,我写了一些touchesBegan代码。想象一下,当您创建它们时,您已将所有图像视图添加到数组(巧妙地命名为imageViews)。你可以改变你的代码看起来像这样:

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

我不确定这是多么昂贵,所以你可能需要对它进行分析,但它应该做你期望的事情。

相关问题