1

I would ideally like to write code that allows me to determine what animations a desired view performs when a certain function is executed, e.g. (n.b., pseudocode):

- (void)animateView:(UIView *)view withAnimations:(NSArray *)arrayOfAnimationBlocks

The above (i.e., desired) function would go through a series of animations in sequence and would not perform each animation until the previous animation has fully been executed. I would also be able to add and remove animations to arrayOfAnimationBlocks during runtime.

To do something like this, I am trying to use the following:

[UIView animateWithDuration:duration animations:animationBlock completion:completionBlock];

and am passing all parameters (duration, animationBlock, completionBlock) when the function is called.

However...

it seems like you cannot access self from within the animationBlock? My animation block contains:

void (^animationBlock)(void) = ^
{
    NSLog(@"[^animationBlock]");
    [self.viewToAnimate setBounds:CGRectMake(self.viewToAnimate.bounds.origin.x, self.viewToAnimate.bounds.origin.y, self.viewToAnimate.bounds.size.width*2, self.viewToAnimate.bounds.size.height*2)];
};

and my completion block contains:

void (^completionBlock)(void) = ^
{
    NSLog(@"[^completionBlock]");
    [UIView animateWithDuration:duration animations:^{
        [self.viewToAnimate setBounds:CGRectMake(self.viewToAnimate.bounds.origin.x, self.viewToAnimate.bounds.origin.y, self.viewToAnimate.bounds.size.width/2, self.viewToAnimate.bounds.size.height/2)];
    } completion:^(BOOL finished){
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Animation Complete" message:@"The previous animations should be fully completed." delegate:self cancelButtonTitle:@"Dismiss" otherButtonTitles:nil];
        alert.alertViewStyle = UIAlertViewStyleDefault;
        [alert show];
    }];
};

and then I of course have:

- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex
{
    if (buttonIndex == 0) NSLog(@"Cancel pressed.");
    else
    {
        NSLog(@"buttonIndex = %i", buttonIndex);
    }
}

In both animationBlock and completionBlock Xcode gives the following red error: (!) Use of undeclared identifier 'self'

Ken M. Haggerty
  • 24,902
  • 5
  • 28
  • 37
  • Where are you declaring those blocks? `self` only exists inside a method; it's a hidden parameter. – jscs Sep 14 '12 at 19:07
  • Hi Josh: These are in my UIViewController .m file. I have a property called viewToAnimate that is connected to my UIViewController .m file via an IBOutlet. – Ken M. Haggerty Sep 14 '12 at 23:10

4 Answers4

2

Josh gave the correct answer in his comments, which I'll elaborate on. The following is invalid:

void (^completionBlock)(void) = ^
{ ... [self something] ... };

@implementation Whatever

...

@end

(as is the same thing with the @implementation placed above the definition of completionBlock) because in the scope in which you declare completionBlock there is no variable named self. self exists only within instance methods of a class and refers to the specific instance that has been called — its value isn't knowable ahead of time in the general case.

So what you probably want (assuming non-ARC; cut out the autorelease if relevant) is something like:

@implementation Whatever

- (dispatch_block_t)completionBlock
{
     return [[^{ ... [self something] ... } copy] autorelease];
}

@end

Which will dynamically generate a block pointing to the appropriate self and return it in per the normal getter rules. All that'll actually happen at runtime is that the packet of information that represents the outside state going into the block will be generated and stored. There's no code generation or anything like that so don't fret about the cost. However you do need the copy because blocks try to live on the stack and hence aren't safe to return without being moved to the heap, which is what copy achieves in this case.

For the UIView-style completion block you'd similarly want something like:

- (void (^)(BOOL))blockThatTakesABool
{
    return [[^(BOOL var){... [self something] ... } copy] autorelease];
}
Tommy
  • 99,986
  • 12
  • 185
  • 204
  • Okay! So I went ahead and did that and everything looks good except that Xcode is complaining `(!) Use of undeclared identifier 'animationBlock'`. I have defined animation block as a `- (dispatch_block_t)animationBlock` function and have defined it in the private `interface` as `- (dispatch_block_t)animationBlock;` – Ken M. Haggerty Sep 15 '12 at 00:51
  • "self exists only within instance methods of a class" Or class methods. Basically any methods – newacct Sep 16 '12 at 05:36
0

You seem to have declared global variables and assigned them to the blocks. Therefore the blocks are defined in the global context and there's no self there as self is a (hidden) argument of a method and therefore only exist within methods.

Also it's useless to use block syntax at global scope. You could as well have written functions instead of blocks. The very reason of the existence of blocks is that in C (and C++ and in Objective-C since it is build upon C/C++) it is impossible to declare/define functions in a nested way.

Here's what blocks are for:

void foo() { ... }

void bar() 
{ 
  ...
  aFun(foo);
  ... 
}

the above is legal but

void bar() 
{ 
   ...
   afun( void foo() { ... } );
   ...
}

is not legal as in C/C++/Objective-C a function cannot be defined inside another function nor inline in an expression.

Many languages let you define functions inline in an expression, which is a very useful thing, especially for functional style programming. But C/C++/Objective-C do not.

That's why blocks have been invented by Apple for Objective-C (and C++ lambdas, very similar to Apple's blocks, were added to C++ in the C++11 redefinition of the language). Blocks are, in fact, anonymous functions that you can define inline in an expression. You would use a block to solve the problem in my second example.

Blocks (and C++ lambdas) provide language native support for the definition of an inline function and the corresponding closure (with a lot of limitations and quirks as closures are also not a native concept in these languages).

They make somewhat easier for you to comply with Greenspun's tenth rule of programming. (/me waiting for someone to realize how useful guaranteed tail call optimization would also be).

Analog File
  • 5,280
  • 20
  • 23
  • So...how do I animate my UIView? I'm sorry, this is wayy too theoretical for me. I have not taken any computer programming classes as my college required prior knowledge of Python for their introductory class (which I did not know at the time and have since limped through). – Ken M. Haggerty Sep 14 '12 at 23:09
0

If you haven't already you should really read Apple's View Programming Guide. Especially the section on Animations.

Here is the sample code straight from that document:

- (IBAction)showHideView:(id)sender
{
    // Fade out the view right away
    [UIView animateWithDuration:1.0
        delay: 0.0
        options: UIViewAnimationOptionCurveEaseIn
        animations:^{
             thirdView.alpha = 0.0;
        }
        completion:^(BOOL finished){
            // Wait one second and then fade in the view
            [UIView animateWithDuration:1.0
                 delay: 1.0
                 options:UIViewAnimationOptionCurveEaseOut
                 animations:^{
                    thirdView.alpha = 1.0;
                 }
                 completion:nil];
        }];
}

Do you see the part with the completion block? They declare/create the block right inline with the calling function. When you are first starting out you should always look at Apple's code and following them as closely as you can. When you get more experience you can branch out and try other ways to do it.

sosborn
  • 14,676
  • 2
  • 42
  • 46
0

Hi all: I came across the StackOverflow post Best Way to Perform Several Sequential UIView Animations? today, which includes an answer by user Yang using CPAnimationSequence! Links are below.

This looks great!

Community
  • 1
  • 1
Ken M. Haggerty
  • 24,902
  • 5
  • 28
  • 37