0

Apple のサンプル タッチ アプリを確認しましたが、問題が解決されていません。サンプル アプリでは、タッチ イベントが発生すると、タッチ イベントが発生した場所にビューを配置しようとします。これにより、ロジックが単純になります。フレームにタッチ位置が含まれているビューを探すだけです。

それは私のシナリオでは機能しません。これが私のシナリオです。

たくさんのサブビューを含むビューがあります。アイデアは、ユーザーがジェスチャーの方向にサブビューの 1 つを投げ飛ばすことができるようにすることです。touchesBeganイベントで、中心がタッチに最も近いビューを見つけてほしい。

次にtouchesEnded、開始イベントと終了イベントによって決まる速度で、同じビューをイベントに移動させます。速度は必ずしも指の速度と同じではないため、Apple がサンプル アプリで行ったように、ビューをタッチ位置に単純に「アタッチ」することはできません。

で識別されたビューにタッチ オブジェクトでフラグを立て、それを使用してイベントtouchesBegan内のタッチ オブジェクトと比較することを考えましたが、うまくいきません。とイベントtouchesEndedのタッチ オブジェクトは同じではありません。touchesBegantouchesEnded

それで、私は何が欠けていますか?移動するビューとタッチの関係を保存するにはどうすればよいですか?

4

3 に答える 3

0

これが私の解決策です。このソリューションでscaledViewは、矢印ビューが移動するビューです。それらはspriteViewのサブクラスである クラス ですUIView

-(spriteView *)findArrowContainingTouch:(UITouch *)touch inView:(UIView *)scaledView atPoint:(CGPoint) touchPoint
//There could be multiple subviews whose rectangles include the point. Find the one whose center is closest.
{
   spriteView * touchedView = nil;
   float bestDistance2 = 9999999999.9;
   float testDistance2 = 0;
   for (spriteView *arrow in scaledView.subviews) {
      if (arrow.tag == ARROWTAG) {
         if (CGRectContainsPoint(arrow.frame, touchPoint)) {
            testDistance2 = [self distance2Between:touchPoint and:arrow.center];
            if (testDistance2<bestDistance2) {
               bestDistance2 = testDistance2;
               touchedView = arrow;
            }
         }
      }
   }
   return touchedView;
}

このメソッドdistance2Betweenは、2 点間の距離の 2 乗を計算します。

-(spriteView *)findArrowTouchedAtLocation:(CGPoint)p inView:(UIView *)scaledView
{
   spriteView * arrow = nil;
   for (spriteView *testArrow in scaledView.subviews) {
      if (testArrow.tag == ARROWTAG) {
         if ((p.x == testArrow.touch.x) && (p.y == testArrow.touch.y)) {
            arrow = testArrow;
         }
      }
   }
   return arrow;
}

には矢印ではないサブビューがあるscaledViewため、定数 ARROWTAG を使用して矢印を識別します。

#pragma mark - Touches
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
   UIView *scaledView = [self getScaledView];
   for (UITouch *touch in touches) {
      CGPoint touchLocation = [touch locationInView:scaledView];
      spriteView * arrow = [self findArrowContainingTouch:touch inView:scaledView atPoint:touchLocation];
      if (!(arrow==nil)) {
      //Record the original location of the touch event in a property, originalTouchLocation, of the arrow instance. Additionally, store the same point on the property touch.
      //Both properties are necessary. The originalTouchLocation will be used in `touchesEnded` and is not available in the `touch` object. So the information is stored separately.
      //The `touch` property, a CGPoint, is stored in order to identify the view. This property is updated by every touch event. The new value will be used by the upcoming event to find the appropriate view.
         arrow.touch = CGPointMake(touchLocation.x, touchLocation.y);
         arrow.originalTouchLocation = CGPointMake(touchLocation.x, touchLocation.y);
         arrow.debugFlag = YES;
         arrow.timeTouchBegan = touch.timestamp;
      }
    }
}

-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
   UIView *scaledView = [self getScaledView];
   for (UITouch *touch in touches) {
      CGPoint touchLocation = [touch locationInView:scaledView];
      CGPoint previousLocation = [touch previousLocationInView:scaledView];
      //previousLocation is used to find the right view. This must be in the coordinate system of the same view used in `touchesBegan`.
      spriteView * arrow = [self findArrowTouchedAtLocation:previousLocation inView:scaledView];
      if (!(arrow==nil)) {
         arrow.touch = CGPointMake(touchLocation.x, touchLocation.y);
      }
   }
}

-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
   UIView *scaledView = [self getScaledView];
   for (UITouch *touch in touches) {
      CGPoint touchLocation = [touch locationInView:scaledView];
      CGPoint previousLocation = [touch previousLocationInView:scaledView];
      spriteView * arrow = [self findArrowTouchedAtLocation:previousLocation inView:scaledView];
      if (!(arrow==nil)) {
         arrow.touch = CGPointMake(touchLocation.x, touchLocation.y);
         float strokeAngle = [self findAngleFrom:arrow.originalTouchLocation to:touchLocation];
         float strokeDistance2 = sqrt([self distance2Between:arrow.originalTouchLocation and:touchLocation]);
         NSTimeInterval timeElapsed = touch.timestamp - arrow.timeTouchBegan;
         float newArrowSpeed = strokeDistance2 / timeElapsed / 100; //might want to use a different conversion factor, but this one works quite well
         arrow.transform = CGAffineTransformMakeRotation(strokeAngle);
         arrow.currentSpeed = newArrowSpeed;
      }
   }
}

-(void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
   UIView *scaledView = [self getScaledView];
   for (UITouch *touch in touches) {
      CGPoint previousLocation = [touch previousLocationInView:scaledView];
      spriteView * arrow = [self findArrowTouchedAtLocation:previousLocation inView:scaledView];
      if (!(arrow==nil)) {
         arrow.originalTouchLocation = CGPointMake(99999.0, 99999.0);
         NSLog(@"Arrow original location erased");
      }
   }
}
于 2012-12-11T20:46:32.657 に答える
0

touch オブジェクトは、実際には と で同じtouchesBeganですtouchesEnded。それがタッチを追跡する方法であることが判明しました。

于 2012-12-19T18:03:35.633 に答える
0

TouchObject は似ていません。タッチしたビュー、位置などでオブジェクトが変化します。詳細はUITouch Classをご確認ください。

提案 1:

UIViewをサブクラス化し、サブクラス内にタッチ デリゲートを追加することで、プロジェクトに同じことを実装しました。そして、通常の UIView の代わりに、このサブクラスのインスタンスを作成しました。

@interface myView : UIView
@end


@implementation myView

//over ride the following methods
 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
}

 - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
}

- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
}

- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event
{
}

@end

提案 2

同じことを行うにはUIPanGestureを使用します。より簡単な方法になります。

UIPanGestureRecognizer *panGesture = [[[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panGestureMoveAround:)] autorelease];
[panGesture setMaximumNumberOfTouches:2];
[panGesture setDelegate:self];
[yourSubView addGestureRecognizer:panGesture];

-(void)panGestureMoveAround:(UIPanGestureRecognizer *)gesture;
{
    UIView *piece = [gesture view];
    [self adjustAnchorPointForGestureRecognizer:gesture];

    if ([gesture state] == UIGestureRecognizerStateBegan || [gesture state] == UIGestureRecognizerStateChanged) {

        CGPoint translation = [gesture translationInView:[piece superview]];
        [piece setCenter:CGPointMake([piece center].x + translation.x, [piece center].y+translation.y*0.1)];
        [gesture setTranslation:CGPointZero inView:[piece superview]];
    }
}

例はこちら

于 2012-12-11T05:26:53.300 に答える