Разрешение одного вызова метода за раз для метода категории ios (@synchronized)
У меня есть UIViewController и Категория для добавления методов в UIViewController. Есть метод в категории:
@implementation UIViewController (AlertAnimationsAndModalViews)
-(void)someAddedMethod
{
UIView *someView;
//do some animation with the view that lasts 3 seconds
//remove the view and return
}
И в любом контроллере представления я могу вызвать этот метод
[self someAddedMethod];
Однако я хочу разрешить запускать этот метод по одному. Например, если я сделаю два звонка один за другим
[self someAddedMethod];//call1
[self someAddedMethod];//call2
я хочу, чтобы второй звонок ждал, пока первый звонок не будет завершен. Я понимаю, что UIView animationWithduration... запускается в отдельном потоке, и, поскольку я не могу создать iVars в категории, я не могу использовать @synchronized(someObject)..
Любой совет?
Заранее спасибо!
РЕДАКТИРОВАТЬ
Метод выглядит так:
-(void)showTopBannerWithHeight:(CGFloat)height andWidth:(CGFloat)width andMessage:(NSString *)message andDuration:(CGFloat)duration
{
UILabel *messageLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, -height, width, height)];
[self.view.superview addSubview:messageLabel];
[UIView animateWithDuration:0.5
delay:0
options: UIViewAnimationOptionBeginFromCurrentState
animations:^{
messageLabel.frame = CGRectMake(0, 0, SCREEN_WIDTH, height);
}
completion:^(BOOL finished){
[UIView animateWithDuration: 0.5
delay:duration
options: UIViewAnimationOptionBeginFromCurrentState
animations:^{
messageLabel.frame = CGRectMake(0, -height, SCREEN_WIDTH, height);
}
completion:^(BOOL finished){
[messageLabel removeFromSuperview];
}];
}];
}
Так что я показываю "баннер" в верхней части экрана, жду некоторое время (CGFloat), затем выдвигаю вид из экрана и удаляю. Поскольку это относится к категории, я не могу добавить переменные экземпляра... поэтому я хочу добиться того, чтобы при выполнении более одного вызова этого метода я хотел, чтобы первый вызов выполнялся без ожидания, но каждый вызов после этого дождитесь окончания предыдущего вызова.
3 ответа
Предполагая, что вы хотите запустить следующую анимацию после завершения предыдущей. Таким образом, вы можете использовать некоторые общие NSMutableArray* _animationQueue
место хранения:
-(void)someAddedMethod
{
NSTimeInterval duration = 3.0;
void (^animationBlock)() = ^{
//do some animations here
self.view.frame = CGRectOffset(self.view.frame, 40, 0);
};
__block void (^completionBlock)(BOOL) = ^(BOOL finished){
[_animationQueue removeObjectAtIndex:0];
if([_animationQueue count]>0) {
[UIView animateWithDuration:duration animations:_animationQueue[0] completion:completionBlock];
}
};
[_animationQueue addObject:animationBlock];
if([_animationQueue count]==1) {
[UIView animateWithDuration:duration animations:animationBlock completion:completionBlock];
}
}
Обратите внимание, вам не нужно никаких @synchronized
особенности, так как все идет в основном потоке.
ОБНОВЛЕНИЕ: приведенный ниже код делает именно то, что вам нужно:
-(void)showTopBannerWithHeight:(CGFloat)height andWidth:(CGFloat)width andMessage:(NSString *)message andDuration:(CGFloat)duration
{
static NSMutableArray* animationQueue = nil;
if(!animationQueue) {
animationQueue = [[NSMutableArray alloc] init];
}
void (^showMessageBlock)() = ^{
UILabel *messageLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, width, height)];
messageLabel.text = message;
[self.view.superview addSubview:messageLabel];
[UIView animateWithDuration: 0.5
delay:duration
options:UIViewAnimationOptionBeginFromCurrentState
animations:^{
messageLabel.frame = CGRectOffset(messageLabel.frame, 0, -height);
}
completion:^(BOOL finished){
[messageLabel removeFromSuperview];
[animationQueue removeObjectAtIndex:0];
if([animationQueue count]>0) {
void (^nextAction)() = [animationQueue objectAtIndex:0];
nextAction();
}
}];
};
[animationQueue addObject:showMessageBlock];
if([animationQueue count]==1) {
showMessageBlock();
}
}
Если речь идет только об анимации, вы можете проверить if ([someView.layer animationForKey:@"sameKeyAsOnCreation"] == nil)
, Чем вы только добавите анимацию, если она в данный момент не запущена.
Вы также можете использовать связанные объекты для хранения состояния самостоятельно (анимация запущена / не запущена).
Попробуйте использовать это. И использовал self
в @synchronized
директивы.
- (void)criticalMethod {
@synchronized(self) {
// Critical code.
}
}
Примечание: @synchronized()
Директива принимает в качестве единственного аргумента любой объект Objective-C, включая self. Этот объект известен как семафор взаимного исключения или мьютекс. Это позволяет потоку заблокировать часть кода, чтобы предотвратить его использование другими потоками.