2

3 つの個別の子ビューを持つ親ビューがあります。子ビューは親内で重なり合うことなく (間にスペースを空けて) 広がっています。ユーザーが画面上で指を (指を離さずに) 動かすと、それぞれの子ビューに出入りするときにタッチを追跡したいと思います。

: ユーザーが子ビューの外側の画面のどこかに触れ始め、次に子 1 を指でスワイプし、子 1 から離して、子 2 を指で離した場合、これらのイベントがトリガーされると予想されます。

  1. タッチが始まりました
  2. タッチ入力子 1
  3. タッチ終了子 1
  4. タッチ入力子 2
  5. タッチ終了

この場合、 touchesBegan:withEvent: および touchesEnded:withEvent: メソッドが役立つように思われますが、子ビュー コントローラーでそれらを定義すると、ユーザーが外部に触れ始めた場合、それらはまさに私が望むことを行いません。子ビューをスワイプしてから子ビューをスワイプすると、子自体でタッチ イベントはトリガーされません。

現在の解決策:私は現在、これを達成するのが本当に難しいと感じる解決策を使用しています。親で touchesBegan:withEvent:、touchesEnded:withEvent:、および touchesMoved:withEvent: を観察し、各イベントの座標を取得し、それらが子の境界内にあるかどうかを判断しています。その場合は、上記の適切なイベントをトリガーします。

この方法はほとんど機能しますが、非常に非効率的です。フレームワークがこの作業を処理する必要があるように感じます。私の状態管理コードは、「開始」または「終了」トリガーを見逃すこともあります。これは、タッチ イベントがドロップされたか、予期しない順序で発生したためだと思われます。ここでより良い方法がありませんか?

4

1 に答える 1

2

The simplest solution would be something like:

- (void)viewDidLoad
{
    [super viewDidLoad];

    UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(pan:)];
    [self.view addGestureRecognizer:pan];

    // Do any additional setup after loading the view.
}

- (void)pan:(UIPanGestureRecognizer *)sender
{
    static NSInteger startViewIndex;
    static NSInteger endViewIndex;
    CGPoint location = [sender locationInView:self.view];

    if (sender.state == UIGestureRecognizerStateBegan)
    {
        if (CGRectContainsPoint(self.view0.frame, location))
            startViewIndex = 0;
        else if (CGRectContainsPoint(self.view1.frame, location))
            startViewIndex = 1;
        else if (CGRectContainsPoint(self.view2.frame, location))
            startViewIndex = 2;
        else 
            startViewIndex = -1;
    }
    else if (sender.state == UIGestureRecognizerStateEnded)
    {
        if (CGRectContainsPoint(self.view0.frame, location))
            endViewIndex = 0;
        else if (CGRectContainsPoint(self.view1.frame, location))
            endViewIndex = 1;
        else if (CGRectContainsPoint(self.view2.frame, location))
            endViewIndex = 2;
        else 
            endViewIndex = -1;

        if (startViewIndex != -1 && endViewIndex != -1 && startViewIndex != endViewIndex)
        {
            // successfully moved between subviews!
            NSLog(@"Moved from %1d to %1d", startViewIndex, endViewIndex);
        }
    }
}

Perhaps a little more elegant would be to define your own custom gesture recognizer (that way if you aren't dragging from one of your subviews, it will fail which will allow other gesture recognizers you might have going on elsewhwere to work ... probably not an issue unless you're use multiple gesture recognizers; it also isolates the gory details of the gesture logic from the rest of your view controller):

@interface PanBetweenSubviewsGestureRecognizer : UIPanGestureRecognizer
{
    NSMutableArray *_arrayOfFrames;
}

@property NSInteger startingIndex;
@property NSInteger endingIndex;

@end

@implementation PanBetweenSubviewsGestureRecognizer

@synthesize startingIndex = _startingIndex;
@synthesize endingIndex   = _endingIndex;

- (void)dealloc
{
    _arrayOfFrames = nil;
}

- (id)initWithTarget:(id)target action:(SEL)action
{
    self = [super initWithTarget:target action:action];
    if (self)
    {
        _arrayOfFrames = [[NSMutableArray alloc] init];
    }
    return self;
}

- (void)addSubviewToArrayOfFrames:(UIView *)view
{
    [_arrayOfFrames addObject:[NSValue valueWithCGRect:view.frame]];
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event 
{
    [super touchesBegan:touches withEvent:event];

    UITouch *touch = [touches anyObject];
    CGPoint location = [touch locationInView:self.view];

    for (NSInteger i = 0; i < [_arrayOfFrames count]; i++)
    {
        if (CGRectContainsPoint([[_arrayOfFrames objectAtIndex:i] CGRectValue], location)) 
        {
            self.startingIndex = i;
            return;
        }
    }

    self.startingIndex = -1;
    self.endingIndex = -1;
    self.state = UIGestureRecognizerStateCancelled;
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
    [super touchesEnded:touches withEvent:event];

    UITouch *touch = [touches anyObject];
    CGPoint location = [touch locationInView:self.view];

    for (NSInteger i = 0; i < [_arrayOfFrames count]; i++)
    {
        if (CGRectContainsPoint([[_arrayOfFrames objectAtIndex:i] CGRectValue], location)) 
        {
            self.endingIndex = i;
            return;
        }
    }

    self.endingIndex = -1;
    self.state = UIGestureRecognizerStateCancelled;
}

@end

Which you could then use as follows:

- (void)viewDidLoad
{
    [super viewDidLoad];

    PanBetweenSubviewsGestureRecognizer *pan = [[PanBetweenSubviewsGestureRecognizer alloc] initWithTarget:self action:@selector(pan:)];
    [pan addSubviewToArrayOfFrames:self.view0];
    [pan addSubviewToArrayOfFrames:self.view1];
    [pan addSubviewToArrayOfFrames:self.view2];

    [self.view addGestureRecognizer:pan];

    // Do any additional setup after loading the view.
}

- (void)pan:(PanBetweenSubviewsGestureRecognizer *)sender
{
    if (sender.state == UIGestureRecognizerStateEnded && sender.startingIndex >= 0 && sender.endingIndex >= 0 && sender.startingIndex != sender.endingIndex)
    {
        // successfully moved between subviews!
        NSLog(@"Moved from %1d to %1d", sender.startingIndex, sender.endingIndex);
    }
}
于 2012-07-12T03:15:52.647 に答える