Как прошить кнопку в потоке пользовательского интерфейса?

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

- (void)flickEmergencyButton {
  // Check whether an emergency is in progress...
  if (model.emergencyInProgress) {
    // ...and if so, flick the state
    self.emergencyButton.selected = !self.emergencyButton.selected;

    // Make this method be called again in 800ms
    [self performSelector:@selector(flickEmergencyButton) withObject:nil afterDelay:0.8];
  } else {
    // ...otherwise, turn the button off
    self.emergencyButton.selected = NO;
  }
}

... который работает очень хорошо, за исключением того, что в пользовательском интерфейсе также есть UIScrollView, и пока пользователь держит на нем палец и прокручивает, кнопка зависает. Хотя я полностью понимаю, почему это так, я не уверен, что с этим делать.

Сообщение performSelector:withObject:afterDelay планирует отправку сообщения в текущем потоке, который является основным потоком, т.е. пользовательский интерфейс и, следовательно, не сможет обработать сообщение до тех пор, пока не будут завершены все другие действия пользовательского интерфейса. Правильный? Но мне нужно сделать это в потоке пользовательского интерфейса, так как я не могу выбрать/отменить выбор кнопки в любом другом потоке, верно? Итак, какое решение здесь?


person McKrassy    schedule 01.08.2011    source источник
comment
Разве вы не можете просто добавить анимацию к кнопке с установленным параметром UIViewAnimationOptionRepeat или UIViewAnimationOptionAutoreverse?   -  person Peter Sarnowski    schedule 01.08.2011


Ответы (2)


Я бы рекомендовал использовать Core Animation. Попробуйте что-то вроде этого:

-(void) flash{
    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationDuration:0.3f];
    [UIView setAnimationCurve:UIViewAnimationCurveLinear];

    if( emergency ){       
        // Start flashing
        [UIView setAnimationRepeatCount:1000];
        [UIView setAnimationRepeatAutoreverses:YES];

        [btn setAlpha:0.0f];        
    }else{
        // Stop flashing
        [UIView setAnimationBeginsFromCurrentState:YES];
        [UIView setAnimationRepeatCount:1];

        [btn setAlpha:1.0f];        
    }
    emergency = !emergency;
    [UIView commitAnimations];
}

Где btn объявлен как

@property(nonatomic, retain) IBOutlet UIButton *btn;

а Emergency — это простая переменная типа BOOL.

Вызов flash для запуска и остановки анимации.

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

Надеюсь, поможет.

ОБНОВЛЕНИЕ:

Что касается перехода между двумя изображениями, попробуйте вызвать imageFlash вместо flash:

-(void) imageFlash{

    CABasicAnimation *imageAnimation = [CABasicAnimation animationWithKeyPath:@"contents"];
    [btn setImage:normalState forState:UIControlStateNormal];

    if( emergency ){        
        imageAnimation.duration = 0.5f;
        imageAnimation.repeatCount = 1000;
    }else{
        imageAnimation.repeatCount = 1;
    }

    imageAnimation.fromValue = (id)normalState.CGImage;
    imageAnimation.toValue = (id)emergencyState.CGImage;    
    [btn.imageView.layer addAnimation:imageAnimation forKey:@"animateContents"];    
    [btn setImage:normalState forState:UIControlStateNormal]; // Depending on what image you want after the animation.

    emergency = !emergency;
}

Где normalState и emergencyState — изображения, которые вы хотите использовать:

Объявлено как:

UIImage *normalState;
UIImage *emergencyState;

Назначение изображений:

normalState = [UIImage imageNamed:@"normal.png"];
emergencyState = [UIImage imageNamed:@"alert.png"];

Удачи!

person Xavi Gil    schedule 01.08.2011
comment
Спасибо тебе за это. Это хорошая идея; почему-то я вообще не подумал о Core Animation... однако моя кнопка состоит из двух изображений: одно для выключения и одно для включения. Есть ли способ анимации перехода между двумя изображениями? - person McKrassy; 02.08.2011

Хотя это похоже на работу для CoreAnimation (возможно, анимация backgroundColor пользовательского UIControl), вы можете выполнить это с NSTimer, работающим в соответствующем режиме цикла выполнения.

e.x.

NSTimer *timer = [NSTimer timerWithTimeInterval:0.8f target:self selector:@selector(flicker:) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];

и когда вы хотите остановить анимацию:

[timer invalidate], timer = nil;
button.selected = NO;

Добавляя таймер ко всем NSRunLoopCommonModes, вы не только добавляете его в режим цикла выполнения по умолчанию, но и в режим, в котором он находится, пока взаимодействие с пользователем постоянно обрабатывается (UITrackingRunLoopMode.)

Документация Apple дает более подробное объяснение режимов циклов выполнения и циклов выполнения в целом.

person Sam    schedule 01.08.2011