0

在研究“如何在移动的 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
4

2 回答 2

2

简短的回答是,你不能。

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

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

如果你想使用 Core Animation,这是没有办法的。

你有几个选择。

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

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

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

于 2013-03-31T14:43:33.830 回答
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]];

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

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

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

于 2013-04-01T17:22:50.497 回答