web-dev-qa-db-fra.com

UIButton ne peut pas être touché lorsqu'il est animé avec UIView animateWithDuration

J'ai le code suivant:

[UIView animateWithDuration:0.3
                      delay:0.0
                    options:UIViewAnimationCurveEaseOut | UIViewAnimationOptionAllowUserInteraction
                 animations:^{
                     CGRect r = [btn frame];
                     r.Origin.y -= 40;
                     [btn setFrame: r];
                 }
                 completion:^(BOOL done){
                     if(done){
                         [UIView animateWithDuration:0.3
                                               delay:1
                                             options:UIViewAnimationOptionCurveEaseIn | UIViewAnimationOptionAllowUserInteraction
                                          animations:^{
                                              CGRect r = [btn frame];
                                              r.Origin.y += 40;
                                              [btn setFrame: r];
                                          }
                                          completion:^(BOOL done){if(done) zombiePopping = 0; }];
                     }

                 }];

Le problème est qu'il semble que le bouton ne réagisse pas tant qu'il est animé, même si j'utilise UIViewAnimationOptionAllowInteraction, ce qui est un peu étrange pour moi. 

Peut-être que cela sera le plus possible avec Core Animation pour fonctionner? et si oui, comment pourrais-je m'y prendre? 

39
Shai Mishali

La partie tactile du bouton ne coïncide pas avec le cadre visible du bouton lorsqu'il est animé. 

En interne, l'image du bouton sera définie sur la valeur finale de votre animation. Vous devriez trouver que vous pouvez taper dans cette zone et cela fonctionnerait. 

Pour appuyer sur un bouton en mouvement, vous devez effectuer un test de frappe sur la propriété .layer.presentationLayer du bouton (vous devez importer le framework QuartzCore pour ce faire). Cela se fait généralement de manière tactile dans votre contrôleur de vue. 

Je suis heureux de développer cette réponse si vous avez besoin de plus.

Voici comment vous répondriez à un événement tactile en testant les couches de présentation. Ce code se trouve dans la sous-classe du contrôleur de vue qui gère vos boutons. Lorsque j’ai fait cela, j’ai été intéressé par le toucher initial plutôt que par le toucher (ce qui est typiquement le moment où un tap serait enregistré), mais le principe est le même.

N'oubliez pas d'importer le cadre QuartzCore et de l'ajouter à votre projet.

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    UITouch *touch = [touches anyObject];
    CGPoint touchLocation = [touch locationInView:self.view];
    for (UIButton *button in self.buttonsOutletCollection)
    {
        if ([button.layer.presentationLayer hitTest:touchLocation])
        {
            // This button was hit whilst moving - do something with it here
            break;
        }
    }
}
52
jrturton

Dans mon cas, j’ai mis superView.alpha = 0, il arrête l’interaction du bouton bien que j’ai configuréUIViewAnimationOptionAllowUserInteraction.

Solution? Facile, il suffit de réduire alpha à 0.1 au lieu de 0

[UIView animateWithDuration:durationTime delay:0 options:UIViewAnimationOptionAllowUserInteraction | UIViewAnimationOptionOverrideInheritedOptions | UIViewAnimationOptionCurveEaseInOut | UIViewAnimationOptionRepeat | UIViewAnimationOptionAutoreverse animations: ^{
            superView.alpha = 0.1; // 0.0 will make the button unavailable to touch
        } completion:nil];
45
nahung89

L'ordre des options est important, vous devez d'abord placer UIViewAnimationOptionAllowUserInteraction, puis ajouter d'autres options.

Dans Swift 3, utilisez: .allowUserInteraction

24
Elvis

J'ai réalisé ceci en utilisant NSTimer: D'abord, lancez l'animation de démarrage, ensuite vous devriez lancer une minuterie, c'est le code de démonstration:

-(void)fun···
{
    [UIView animateWithDuration:0.3
                      delay:0.0
                    options:UIViewAnimationCurveEaseOut |  UIViewAnimationOptionAllowUserInteraction
                 animations:^{
                     CGRect r = [btn frame];
                     r.Origin.y -= 40;
                     [btn setFrame: r];
                 }
                 completion:^(BOOL done){}];

    then , you should start an timer,example:
    //start timer
   if (timer != nil)
   {
       [timer invalidate];
       timer = nil;
   }
   [self performSelector:@selector(closeButton) withObject:nil afterDelay:0];
}


- (void)closeButton
{
    if (timer != nil)
    {
        return;
    }

    //start timer
    timer = [NSTimer scheduledTimerWithTimeInterval:1.5f target:self selector:@selector(timerEvent) userInfo:nil repeats:NO];
    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
}

- (void)timerEvent
{
    [UIView animateWithDuration:0.3
                                               delay:1
                                             options:UIViewAnimationOptionCurveEaseIn | UIViewAnimationOptionAllowUserInteraction
                                          animations:^{
                                              CGRect r = [btn frame];
                                              r.Origin.y += 40;
                                              [btn setFrame: r];
                                          }
                                          completion:^(BOOL done){if(done) zombiePopping = 0; }];
    [timer invalidate];
    timer = nil;
}
1
Xu Junwen

Même réponse dans Swift (2.0), pour les paresseux. Remplacez-le par une collection de boucles et de sorties si nécessaire:

override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
    let touch = touches.first
    let touchLocation = touch!.locationInView(self.view)
    if self.button.layer.presentationLayer()!.hitTest(touchLocation) != nil {
        action() // Your action, preferably the original button action.
    }
}
1
Schemetrical

Swift 3.0 Créez un aperçu en haut dans viewdidload

let superView = UIView(frame: view.frame)
    superView.isUserInteractionEnabled = true
    superView.backgroundColor = UIColor.clear
    superView.alpha = 0.1
    view.addSubview(superView)

maintenant mettre en œuvre touchesbagan comme ça 

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {

    guard let touch = (touches as NSSet).anyObject() as? UITouch else {
        return
    }

    let touchLocation = touch.location(in: self.view)
    if btnone.layer.presentation()?.hitTest(touchLocation) != nil {
        print("1") // Do stuff for button 
    }
    if btntwo.layer.presentation()?.hitTest(touchLocation) != nil {
        print("2") // Do stuff
    }
    if btnthree.layer.presentation()?.hitTest(touchLocation) != nil {
        print(3) // Do stuff
    }
    if btnfour.layer.presentation()?.hitTest(touchLocation) != nil {
        print(4) // Do stuff
    }
}
1
Sekhar22

Version Swift: '999' est la valeur de tag utilisée pour identifier la vue cible.

override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
        let touch = touches.first
        let touchLocation = (touch?.locationInView(self.rootView))!

        for view in self.view.subviews {
            if let layer = view.layer.presentationLayer()?.hitTest(touchLocation) where view.tag == 999 {
                // Here view is the tapped view
                print(view)
            }
        }
    }
0
Mahmoud Shahoud

fonctionne comme un charme

override func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? { if bounds.contains(point) && !hidden && userInteractionEnabled && enabled { return self } return super.hitTest(point, withEvent: event) }

Bien que ceci la réponse soit également correcte

0
Tim