0

「動くUIImageViewでタッチイベントを検出するにはどうすればよいですか?」を調査するとき 私はいくつかの答えに出くわし、それらを自分のアプリに実装しようとしました。私が遭遇したものは何も機能していないようです。私がやろうとしていることを説明してから、コードを投稿します。ご意見、ご提案、コメント、または回答をお待ちしております。

私のアプリには、左から右に画面全体に浮かぶカードがいくつかあります。これらのカードはさまざまな色で、ゲームの目的はカードを同じ色の対応するコンテナにドラッグすることです。ユーザーがカードを十分な速さでタッチしてドラッグしないと、カードが画面から外れてしまい、ポイントが失われます。正しいコンテナに含まれるカードが多いほど、スコアが高くなります。

カードが左から右にフロートするように、コア アニメーションを使用してコードを記述しました。これは機能します。ただし、カードに触れてコンテナに向かってドラッグしようとすると、カードの UIImageView に触れていることが正しく検出されません。

カードを移動するコードが適切に実装されているかどうかをテストするために、動かないカードの移動を可能にするコードもいくつか書きました。この場合、私のタッチが検出され、それに応じて動作します。

動かないカードしか操作できないのはなぜですか? これをかなり調査した後、コードは次のようです。

 options:UIViewAnimationOptionAllowUserInteraction

私の動くUIImagesを検出するための重要な要素です。しかし、私はこれを試してみましたが、効果はないようです。

私が間違っているかもしれないもう1つの重要なことは、正しいプレゼンテーションレイヤーを適切に利用していないことです。このようなコードをプロジェクトに追加しました。また、動かないオブジェクトでのみ作業します。

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. すべてのアニメーションを含む親ビューにジェスチャ レコグナイザーを追加し、親ビューのプレゼンテーション ビューでレイヤー ヒット テストを使用して、タップされたアニメーション レイヤーを特定し、そのレイヤーのデリゲートをフェッチします。これがビューになります。あなたはアニメーション化しています。この 2 番目の手法を実行する方法を示すプロジェクトが github にあります。単一の移動ビューでのタップのみを検出しますが、基本は表示されます: Core Animation demo project on github .

(この投稿が役に立った場合は、常に投票をお願いします)

于 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 に答える