UITapGestureRecognizer не отвечает на анимированное подвид

У меня есть простая программа, которая создает подвид и анимирует его на экране.

В рамках этой программы я хотел бы добавить функциональность при нажатии на подвид. Я использую следующий метод для создания подпредставления, добавляю UITapGestureRecognizer и затем анимирую подпредставление:

int randomName = arc4random() % ([pieceNames count] - 1);
int animationDuration = arc4random() %  5 + 5 ;
NSString *randomPiece = [pieceNames objectAtIndex:randomName];

float yStart = arc4random() % 650;
float yEnd = arc4random() % 650;

UIView *piece = [[PieceView alloc]initWithFrame:CGRectMake(100.0, yStart, 75.0, 75.0)];
[piece setValue:randomPiece forKey:@"name"];

UITapGestureRecognizer *recognizer = [[UITapGestureRecognizer alloc]initWithTarget:self
                                                                            action:@selector(handleTouch:)];
[piece addGestureRecognizer:recognizer];

[[self view] addSubview:piece];

[UIView animateWithDuration:animationDuration
                      delay:0.0
                    options:UIViewAnimationOptionAllowUserInteraction
                 animations:^(void){
                     piece.center = CGPointMake(950.0, yEnd);
                 } completion:^(BOOL done){
                     [piece removeFromSuperview];
                 }];

Вот код, который обрабатывает тап:

PieceView *pv = (PieceView *) recognizer.view;
NSLog(@"%@ was tapped", pv.name);

Что происходит, так это то, что при прикосновении к PieceView программа не отвечает. Однако, если я удаляю блок анимации, программа реагирует на нажатие.

Почему UITapGestureRecognizer не отвечает на PieceView, когда он анимирован?


person vonbaron    schedule 27.08.2013    source источник
comment
Вы пытаетесь коснуться представления до того, как анимация будет завершена, верно? Потому что в противном случае представление, очевидно, не будет отвечать, поскольку оно больше не находится в иерархии представлений...   -  person David Ganster    schedule 27.08.2013


Ответы (2)


Я боролся с той же проблемой, и она сводится к следующему: анимированный вид всегда находится только в двух местах: в начальной и конечной позиции. Базовая анимация просто визуализирует слой представления в интерполированных позициях между начальной и конечной точками в течение определенного периода времени.

Это почти как когда вы смотрите на звезды и понимаете, что то, что вы видите, на самом деле не то, что происходит прямо сейчас. :)

К счастью, решение довольно простое. Вы можете поместить распознаватель касания в суперпредставление, а затем проверить presentationLayer вашего анимированного представления (который действительно дает вам точный кадр в любой момент времени), чтобы определить, является ли ваше касание попаданием или нет.

Я построил простой UIViewController, который демонстрирует как проблему, так и решение:

#import <UIKit/UIKit.h>

@interface MSMViewController : UIViewController

@end

И реализация:

#import "MSMViewController.h"

@interface MSMViewController ()
@property (nonatomic, strong) UIView *animatedView;
@end

@implementation MSMViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    CGRect startFrame = CGRectMake(125, 0, 70, 70);
    CGRect endFrame = CGRectMake(125, 400, 70, 70);

    // draw a box to show where the animated view begins
    UIView *startOutlineView = [[UIView alloc] initWithFrame:startFrame];
    startOutlineView.layer.borderColor = [UIColor blueColor].CGColor;
    startOutlineView.layer.borderWidth = 1;
    [self.view addSubview:startOutlineView];

    // draw a box to show where the animated view ends
    UIView *endOutlineView = [[UIView alloc] initWithFrame:endFrame];
    endOutlineView.layer.borderColor = [UIColor blueColor].CGColor;
    endOutlineView.layer.borderWidth = 1;
    [self.view addSubview:endOutlineView];

    self.animatedView = [[UIView alloc] initWithFrame:startFrame];
    self.animatedView.backgroundColor = [UIColor yellowColor];
    [self.view addSubview:self.animatedView];

    [UIView animateWithDuration:10 delay:2 options:UIViewAnimationOptionAllowUserInteraction animations:^{

        self.animatedView.frame = endFrame;

    } completion:nil];

    // this gesture recognizer will only work in the space where endOutlintView is
    UITapGestureRecognizer *boxTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(boxTap:)];
    [self.animatedView addGestureRecognizer:boxTap];

    // this one will work
    UITapGestureRecognizer *superviewTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(superviewTap:)];
    [self.view addGestureRecognizer:superviewTap];

}

- (void)boxTap:(UITapGestureRecognizer *)tap {
    NSLog(@"tap. view is at %@", NSStringFromCGPoint(self.animatedView.frame.origin));
}

- (void)superviewTap:(UITapGestureRecognizer *)tap {
    CGRect boxFrame = [self.animatedView.layer.presentationLayer frame];
    if (CGRectContainsPoint(boxFrame, [tap locationInView:self.view])) {
        NSLog(@"we tapped the box!");
    }
}

@end
person greenisus    schedule 17.03.2014
comment
Спасибо за подробное объяснение. - person vonbaron; 23.03.2014

Решение намного проще, вам просто нужно установить параметр анимации UIViewAnimationOptions.allowUserInteraction.

UIView.animate(withDuration: duration, delay: 0.1, options: [.allowUserInteraction], animations: {
   ...
}, completion: { (completed) in
   ...
}
person Luc Dion    schedule 13.09.2018