UIButton нельзя трогать во время анимации с помощью UIView animateWithDuration - PullRequest
42 голосов
/ 01 декабря 2011

У меня есть следующий код:

[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; }];
                     }

                 }];

Проблема в том, что кнопка не реагирует на прикосновения во время анимации, хотя я использую UIViewAnimationOptionAllowInteraction, что немного странно для меня.

Может быть, это будет сделано с Core Animation для работы? и если так, как бы я поступил об этом?

Ответы [ 9 ]

52 голосов
/ 01 декабря 2011

Сенсорная часть кнопки не будет совпадать с видимой рамкой кнопки во время ее анимации.

Внутри рамки кнопки будет установлено окончательное значение из вашей анимации.Вы должны найти, что вы можете нажать в этой области, и это будет работать.

Чтобы нажать движущуюся кнопку, необходимо выполнить тестирование нажатия на свойстве .layer.presentationLayer кнопки (для этого необходимо импортировать инфраструктуру QuartzCore).Обычно это делается с помощью методов обработки касаний в вашем контроллере представления.

Я буду рад продолжить этот ответ, если вам понадобится больше.

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

- (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;
        }
    }
}
50 голосов
/ 28 июня 2015

В моем случае, я установил superView.alpha = 0, он останавливает взаимодействие кнопки, хотя я установил UIViewAnimationOptionAllowUserInteraction.

Решение?Легко, просто уменьшите alpha до 0,1 вместо 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];
27 голосов
/ 24 апреля 2015

Порядок опций имеет значение, сначала нужно поставить UIViewAnimationOptionAllowUserInteraction, затем добавить другие опции.

В Swift 3 используйте: .allowUserInteraction

1 голос
/ 24 октября 2016

Swift 3.0 Создание суперпредставления сверху в viewdidload

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

теперь реализуем touchesbagan вот так

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 голос
/ 15 июля 2015

Тот же ответ в Swift (2.0), для ленивых.При необходимости заменить на петлю и сборник розеток:

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 голос
/ 21 декабря 2012

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

-(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;
}
0 голосов
/ 18 марта 2019

Если у вас есть подкласс UIButton, самый простой способ - переопределить hitTest, как

public override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
        return self.layer.presentation()?.hitTest(self.convert(point, to: superview)).flatMap { _ in return self } ?? nil
    }
0 голосов
/ 19 августа 2016

работает как брелок

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

Хотя этот ответ также правильный

0 голосов
/ 23 июня 2016

Версия Swift: '999' - это значение тега, используемое для идентификации целевого вида.

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)
            }
        }
    }
...