【问题标题】:How do I detect a touch event on a moving UIImageView?如何检测移动 UIImageView 上的触摸事件?
【发布时间】:2013-03-30 19:54:31
【问题描述】:

在研究“如何在移动的 UIImageView 上检测触摸事件?”时我遇到了几个答案,并尝试将它们实施到我的应用程序中。我遇到的任何事情似乎都不起作用。我将解释我正在尝试做什么,然后发布我的代码。任何想法、建议、cmets 或答案都非常感谢!

我的应用有几张卡片从左到右在屏幕上浮动。这些卡片有各种颜色,游戏的目标是将卡片向下拖动到颜色相似的相应容器中。如果用户没有足够快地触摸和拖动卡片,卡片将简单地从屏幕上飘下来,并且会丢失积分。正确容器中包含的卡片越多,得分就越高。

我使用核心动画编写了代码,让我的卡片从左到右浮动。这行得通。但是,当尝试触摸卡片并将其拖向容器时,它没有正确检测到我正在触摸卡片的 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

【问题讨论】:

  • 看看我的回答和讨论here。它可能不是特定于您的需求,但我认为它会有所启发。
  • 我检查了那个代码,我只能让火箭来回移动。当我点击它时,什么也没有发生。它不会停止。它不会消失。

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


【解决方案1】:

简短的回答是,你不能。

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

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

如果你想使用核心动画,这是没有办法的。

你有几个选择。

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

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

(如果您觉得这篇文章对您有帮助,请务必支持投票)

【讨论】:

  • 感谢您的洞察!我已经调整了我的代码,让手势识别器与飞行中的动画进行交互。我仍然无法获得要与之交互的其他视图。我必须设置一组视图(或层)并循环浏览它们以使代码适用于每个视图吗?
【解决方案2】:

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

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

更新

为了帮助您在 cmets 中提出后续问题,我编写了 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!");
    }
  }
}

我不确定这有多贵,因此您可能需要对其进行分析,但它应该可以满足您的期望。

【讨论】:

  • 太棒了!这正是我一直在寻找的。如果我有多个彼此部分重叠的移动形状怎么办?这会按预期返回图层吗?示例:如果一个移动的三角形与一个移动的正方形略微重叠并且我在重叠期间触摸了三角形,我可以让它说“你触摸了三角形”。然后当触摸正方形的可见部分时,它会“你触摸了正方形!”。
  • 是的。这正是它的工作方式。如果您阅读有关命中测试核心动画层的文档,它会说返回包含指定点的层层次结构(包括其自身)中接收器的最远后代。 developer.apple.com/library/mac/documentation/graphicsimaging/…:
  • 当尝试以编程方式向应用程序添加额外的 _imageView 时,我可能做错了什么。我这样做: [[_imageView alloc] initWithFrame: CGRectMake (100,100,100,100)];然后我添加图像 _imageView.image = [UIImage imageName:@"newImage.png"]。然后我将它添加到视图的子视图 [self.view addSubview: _imageView]。当我点击它时,它说“背景点击”。我有 2 个动态图像(你的和我的)。你的说“你触摸了一个形状!”我的说“背景触及”。为什么会这样?
猜你喜欢
  • 2010-10-25
  • 2020-12-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-15
相关资源
最近更新 更多