views:

3407

answers:

2

I have a barButtonItem that calls an action which swaps two views in an animation block. I disable the barButtonItem at the beginning of the action method that it calls, and the I want to enable it when its finished, so that the user can't flip the view again before it has finished flipping.

It does this in a UIView animation block, so If I enable it at the ned of the method, it will be instant and that destroys the purpose because it gets enabled before the animation finishes. In the method, I cast sender to a UIBarButtonItem, and I disable that. So I found [UIView setAnimationDidStopSelector:@selector()], but I need to pass my barButtonItem in the selector so the enableControl Method will enable that (this would be a lot easier if I could make an anonymous function..). So how can I pass the barButton as an argument? I'd rather not have to make an ivar just for this.. Here's the code:

- (void)barBtnItemSwitchViews_Clicked:(id)sender {
    UIBarButtonItem *barButton = (UIBarButtonItem *)sender;
    barButton.enabled = NO;
    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationDuration:0.8];
    [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
    if(!self.yellowVC.view.superview) {
     [UIView setAnimationTransition:UIViewAnimationTransitionFlipFromRight forView:innerView cache:YES];
     [yellowVC viewWillAppear:YES];
     [blueVC viewWillDisappear:YES];
     [blueVC.view removeFromSuperview];
     self.blueVC = nil;
     [innerView addSubview:yellowVC.view];
    } else if(!self.blueVC.view.superview) {
     [UIView setAnimationTransition:UIViewAnimationTransitionFlipFromRight forView:innerView cache:YES];
     [blueVC viewWillAppear:YES];
     [yellowVC viewWillDisappear:YES];
     [yellowVC.view removeFromSuperview];
     self.yellowVC = nil;
     [innerView addSubview:blueVC.view];
    }
    [UIView commitAnimations];
    [UIView setAnimationDidStopSelector:@selector(enableControl:)]; // How do I pass barButton??
}

- (void)enableControl:(UIControl *)control {
    control.enabled = YES;
}

Thanks!!

invalidname: Here's what I changed it to, but the method isn't being called.. What am I doing wrong?

- (void)barBtnItemSwitchViews_Clicked:(id)sender {
    UIBarButtonItem *barButton = (UIBarButtonItem *)sender;
    barButton.enabled = NO;
    [UIView beginAnimations:nil context:barButton];
    [UIView setAnimationDuration:0.8];
    [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
    if(!self.yellowVC.view.superview) {
     [UIView setAnimationTransition:UIViewAnimationTransitionFlipFromRight forView:innerView cache:YES];
     [yellowVC viewWillAppear:YES];
     [blueVC viewWillDisappear:YES];
     [blueVC.view removeFromSuperview];
     self.blueVC = nil;
     [innerView addSubview:yellowVC.view];
    } else if(!self.blueVC.view.superview) {
     [UIView setAnimationTransition:UIViewAnimationTransitionFlipFromRight forView:innerView cache:YES];
     [blueVC viewWillAppear:YES];
     [yellowVC viewWillDisappear:YES];
     [yellowVC.view removeFromSuperview];
     self.yellowVC = nil;
     [innerView addSubview:blueVC.view];
    }
    [UIView setAnimationDidStopSelector:@selector(flipViewAnimationDidStop:finished:context:)];
    [UIView commitAnimations];
}

- (void)flipViewAnimationDidStop:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context {
    UIBarButtonItem *barButton = (UIBarButtonItem *)context;
    barButton.enabled = NO;
    NSLog(@"Disabled");
}

EDIT: I figured it out. I had to make the button the context, and then do setAnimationDelegate:self, and then setAnimationDidEndSelector:@selector(myMethod:finished:context:), and then in the method cast context to uibarbuttonitem and re-enable it. Thanks for your answers!

+1  A: 

I dont think you can pass more than one argument to the selector by using @selector, however you could make your button a class variable and enable or disable it like that in the selector (instead of it being passed as an argument), to define a selector with multiple arguments you can use NSInvocation, like so

NSInvocation *inv = [[NSInvocation alloc] init];
[inv setSelector:@selector(foo:bar:)];
[inv setArgument:123 atIndex:0];
[inv setArgument:456 atIndex:1];

But I dont think this will work for what you are trying to do.

Daniel
First of all I did not say I need multiple arguments, I only wanted one. Secondly, I said I did not want to have to use an ivar (or class var)
Mk12
What i meant is you cant pass aribitrary args into the selector
Daniel
+2  A: 

From UIView's setAnimationDidStopSelector: docs:

The selector should be of the form: - (void)animationDidStop:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context.

The third argument, context, is an object of your choice, set in beginAnimations:context:. So it seems like what you're missing is to use barButton as the context argument, and then rewrite enableControl with a proper signature. In that method, you take the third argument, context, and cast it from void* to a UIBarButtonItem*.

invalidname
Thanks only thing you didn't mention is you need to do [UIView setAnimationDelegate:self] first.
Mk12