5

I have a menu with items popping right after each other in intervals of 3 seconds, I'm doing it like so:

for(UIButton *menuItem in menuItems){
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (0.3 * i) * NSEC_PER_SEC), dispatch_get_current_queue(), ^{
        [menuItem setAlpha:1.0];
    }                             
}

Is it possible to stop the animation in the middle (when a button is touched, for example) ? I tried just setting everything to alpha 1.0 but as expected, the threads kept running and shows the items again.

Would appreciate any ideas:)
Shai.

Shai Mishali
  • 9,224
  • 4
  • 56
  • 83
  • Unrelated to your question but is UIKit thread safe now? – Rog Nov 15 '11 at 21:43
  • I dont really know what that means :> – Shai Mishali Nov 15 '11 at 21:46
  • Unless things have changed on iOS5 (and I don't think they have) you are only supposed to update your UI via the main thread. Your dispatch queues are updating the UI on a background thread and this is likely to cause instability issues with your app. Here's something from the official documentation (bottom of the page) http://developer.apple.com/library/ios/#documentation/uikit/reference/UIKit_Framework/Introduction/Introduction.html#//apple_ref/doc/uid/TP40006955-CH1-SW1 – Rog Nov 15 '11 at 21:49
  • I see. I actually couldn't find a nicer way to do what i want. (I want to display my menu items at intervals of 0.3 seconds). Any other technique you would use? Thanks alot for pointing this out :) – Shai Mishali Nov 15 '11 at 21:52
  • 1
    Your question is still valid and a good one. I'll have a think about it and post something here if I can help. – Rog Nov 15 '11 at 21:54

3 Answers3

3

You could do the animations using UIView block-based animations, with the delay: set. No thread worries there.

These animations can be interrupted by starting another block-based animation with the option UIViewAnimationOptionBeginFromCurrentStateset.

For alpha transitions this is fine since it doesn't matter what the current position of the animation is. If you are moving objects, you can get the current position by querying the view.layer.presentationLayer which holds the current state of the object. You will need to import the QuartzCore framework to do this. This also gives you a method, removeAllAnimations which will do pretty much what it sounds like and takes any view right to the endpoint of any animations it has got pending or active.

jrturton
  • 118,105
  • 32
  • 252
  • 268
  • The problem i'm not just playing the alpha, i'm also playing a sound with CocoDenshion. I'll try your method and see if it works. Thanks man :) – Shai Mishali Nov 16 '11 at 08:33
  • the following code shows all items at the same time instead with delays, for some reason: http://pastebin.com/dVEzCEWr – Shai Mishali Nov 16 '11 at 08:42
  • The completion block isn't executed until the end of the animation. Increment i in your for loop, before each animation is set. I was about to add that you could play your sound in the completion block, if the animation is interrupted the BOOL parameter will be NO so you know not to play the sound. – jrturton Nov 16 '11 at 09:04
  • I didn't really understand why incrementing the `i` within the completion block is wrong, since - i need to increment it by 1 only when the animation ends, no ? Anyways your solution worked perfect, thank you for your kind assistance :) – Shai Mishali Nov 16 '11 at 10:58
  • It only lets me give the bounty in 10 hours so ... :) sorry in advance – Shai Mishali Nov 16 '11 at 11:05
  • I can wait! It's wrong in the completion block because that doesn't get executed until the animation has finished, but you are queuing up all the animations in one go, so the value of i is the same for each animation you are kicking off. So, you need to increment it between the calls to kick off each animation. It is probably out of scope by the time it is executed in the completion block as well... – jrturton Nov 16 '11 at 11:27
1

I don't think you can stop animations already scheduled. But you could set an instance variable with a BOOL flag indicating if a button was already pressed or not.

For example, supposing your button's targets is the method buttonTapped.

Initialize you flag to NO.

- (id)init {
    if (self = [super init]) {
        buttonAlreadyTapped = NO;
    }

    return self;
}

In your button's targets set the flag to YES.

- (void)buttonTapped {
    self.buttonAlreadyTapped = YES;
}

And only modify the alpha if the flag is NO.

for (NSInteger i = 1; i <= [menuItems count]; i++) {        
    UIButton *menuItem = [menuItems objectAtIndex:i-1];

    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (3 * i) * NSEC_PER_SEC), dispatch_get_current_queue(), ^{
        if (!self.buttonAlreadyTapped) {
            menuItem.alpha = 1.0f;
        }
    });
}

Remember that sending messages to UIKit, should always be done on the main thread.

for (NSInteger i = 1; i <= [menuItems count]; i++) {        
    UIButton *menuItem = [menuItems objectAtIndex:i-1];

    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (3 * i) * NSEC_PER_SEC), dispatch_get_current_queue(), ^{
        if (!self.buttonAlreadyTapped) {
            dispatch_async(dispatch_get_main_queue(), ^{
                menuItem.alpha = 1.0f;
            });
        }
    });
}
pablasso
  • 2,479
  • 2
  • 26
  • 32
  • I'm already doing that , but it doesn't really help. I'm showing 6 menu items one-by-one, and when the user clicks on one of them i want to stop the entry animation and show all of them, and cancel the animations in the queue – Shai Mishali Nov 16 '11 at 08:40
  • +1 for the main thread tip , didn't know that (dispatch_async in dispatch_after) . Thanks man :) – Shai Mishali Nov 16 '11 at 11:06
0

Maybe you will find answer in this response and maybe you will do your stuff in other way.

Community
  • 1
  • 1
Serhii Mamontov
  • 4,942
  • 22
  • 26
  • This comment mainly tells me the dispatch_async approach isn't the right way, it doesnt really give an alternative unless we're talking about [NSThread performSelector], and even then , i wouldn't have an answer to my question above (stopping animations already scheduled). – Shai Mishali Nov 15 '11 at 22:01
  • 1
    Also, even though i appreciate your time - i dont think a sentence and a link are considered an answer. Should be a comment next time. Again, thank you for your time regardless. – Shai Mishali Nov 15 '11 at 22:03
  • No problem. I find this gist source code of NSObject category for delayed and canceled blocks https://gist.github.com/955123 If you will use those methods to create blocks instances and try save them, than you can call cancelBlock: method to cancel them. – Serhii Mamontov Nov 15 '11 at 22:28