Как прошить кнопку в потоке пользовательского интерфейса? - PullRequest
1 голос
/ 01 августа 2011

У меня на пользовательском интерфейсе есть кнопка, которую я хотел бы мигать (включать, а затем снова выключать) каждые 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 планирует отправку сообщения в текущем потоке, который является основным, т. Е. пользовательский интерфейс и, следовательно, не может обработать сообщение, пока все другие действия пользовательского интерфейса не закончатся. Правильный? Но мне нужно сделать это в потоке пользовательского интерфейса, так как я не могу выбрать / отменить выбор кнопки в любом другом потоке, верно? Так в чем же здесь решение?

Ответы [ 2 ]

4 голосов
/ 01 августа 2011

Я бы порекомендовал использовать 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;

, а чрезвычайная ситуация - простая переменная BOOL.

Вызовите flash, чтобы запустить и остановитьanimation.

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

Надеждаэто помогает.

ОБНОВЛЕНИЕ:

Что касается перехода между двумя изображениями, попробуйте позвонить 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"];

Удачи!

3 голосов
/ 01 августа 2011

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

ex

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 дают более подробное объяснение режимов цикла выполнения и циклов выполнения вобщий.

...