Allowing one method to be called simultaneously to a method of the ios category (@synchronized)

I have a UIViewController and a Category to add methods to a UIViewController. There is a method in the category:

@implementation UIViewController (AlertAnimationsAndModalViews) -(void)someAddedMethod { UIView *someView; //do some animation with the view that lasts 3 seconds //remove the view and return } 

And in any controller view, I can call this method

 [self someAddedMethod]; 

However, I want this method to run one at a time. For example, if I make two calls one by one

 [self someAddedMethod];//call1 [self someAddedMethod];//call2 

I want the second call to wait for the completion of the first call. I understand that the animation of UIViewWithduration ... is executed in a separate thread, and, seeing that I cannot create iVars in category i, I cannot use @synchronized (someObject) ..

Any tips?

Thanks in advance!

EDIT

The method is as follows:

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

}

So, I show the β€œbanner” at the top of the screen, wait for the duration (CGFloat), then shift the view from the screen and delete it. Since this is in a category, I cannot add instance variables. therefore, I want to ensure that if more than one call to this method is made, I want the first call to be made without waiting, but each call after this should wait until the previous call ends.

+6
source share
3 answers

Assuming you want to start the next animation after the previous one is finished. This way you can use some common 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]; } } 

Please note: you do not need any @synchronized functions, since everything goes along the main thread.

UPDATE: the code below does exactly what you need:

 -(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(); } } 
+1
source

If this is just an animation, you can check if ([someView.layer animationForKey:@"sameKeyAsOnCreation"] == nil) . You will then add the animation if it is not running.

You can also use related objects to store state yourself (animation works / does not work).

+2
source

Try using this one. And use the self directive in @ synchronized .

 - (void)criticalMethod { @synchronized(self) { // Critical code. } } 

Note. The @synchronized() directive takes as a single argument any Objective-C object, including self. This object is known as a mutual exclusion or mutex semaphore . It allows a thread to block a section of code to prevent it from being used by other threads.

0
source

Source: https://habr.com/ru/post/945661/


All Articles