【问题标题】:How to use reference self in Objective-C blocks for animation blocks?如何在 Objective-C 块中使用引用自身作为动画块?
【发布时间】:2012-09-14 18:57:11
【问题描述】:

理想情况下,我想编写代码,让我能够确定在执行某个功能时所需的视图执行哪些动画,例如(注,伪代码):

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

上述(即所需的)函数将依次执行一系列动画,并且在前一个动画完全执行之前不会执行每个动画。我还可以在运行时向arrayOfAnimationBlocks 添加和删除动画。

要做这样的事情,我正在尝试使用以下内容:

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

并且在调用函数时传递所有参数(durationanimationBlockcompletionBlock)。

但是...

您似乎无法从动画块中访问self?我的动画块包含:

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

我的完成块包含:

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

然后我当然有:

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

animationBlockcompletionBlock Xcode 中都会出现以下红色错误: (!) Use of undeclared identifier 'self'

【问题讨论】:

  • 你在哪里声明这些块? self 只存在于方法内部;这是一个隐藏参数。
  • 嗨乔希:这些在我的 UIViewController .m 文件中。我有一个名为 viewToAnimate 的属性,它通过 IBOutlet 连接到我的 UIViewController .m 文件。

标签: objective-c animation block


【解决方案1】:

Josh 在他的 cmets 中给出了正确答案,我将详细说明。以下内容无效:

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

@implementation Whatever

...

@end

(与放在completionBlock 定义上方的@implementation 相同)因为在您声明completionBlock 的范围内没有名为self 的变量。 self 仅存在于类的实例方法中,并引用已调用的特定实例——在一般情况下,它的值无法提前知道。

所以您可能想要的(假设非 ARC;如果相关,则删除自动释放)是这样的:

@implementation Whatever

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

@end

这将动态生成一个指向适当自身的块,并按照正常的 getter 规则将其返回。在运行时实际发生的只是生成并存储代表进入块的外部状态的信息包。没有代码生成或类似的东西,所以不要担心成本。但是,您确实需要 copy,因为块会尝试在堆栈上存在,因此如果不移动到堆中就不能安全返回,这就是 copy 在这种情况下实现的。

对于 UIView 样式的完成块,您同样需要类似的内容:

- (void (^)(BOOL))blockThatTakesABool
{
    return [[^(BOOL var){... [self something] ... } copy] autorelease];
}

【讨论】:

  • 好的!所以我继续这样做,一切看起来都很好,除了 Xcode 抱怨(!) Use of undeclared identifier 'animationBlock'。我已将动画块定义为 - (dispatch_block_t)animationBlock 函数,并在私有 interface 中将其定义为 - (dispatch_block_t)animationBlock;
  • “self 只存在于类的实例方法中”或类方法。基本上任何方法
【解决方案2】:

您似乎已经声明了全局变量并将它们分配给块。因此,这些块是在全局上下文中定义的,并且没有 self,因为 self 是方法的(隐藏)参数,因此只存在于方法中。

在全局范围内使用块语法也是没有用的。您也可以编写函数而不是块。块存在的真正原因是在 C 中(以及 C++ 和 Objective-C,因为它是基于 C/C++ 构建的)不可能以嵌套方式声明/定义函数。

以下是块的用途:

void foo() { ... }

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

以上是合法的,但是

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

不合法,因为在 C/C++/Objective-C 中,函数不能在另一个函数中定义,也不能在表达式中内联。

许多语言允许您在表达式中内联定义函数,这是一个非常有用的东西,尤其是对于函数式编程。但是 C/C++/Objective-C 没有。

这就是 Apple 为 Objective-C 发明块的原因(C++ lambdas,与 Apple 的块非常相似,在 C++11 语言的重新定义中被添加到 C++ 中)。实际上,块是可以在表达式中内联定义的匿名函数。在我的第二个示例中,您将使用块来解决问题。

块(和C++ lambdas)为定义内联函数和相应的闭包提供语言原生支持(有很多限制和怪癖,因为闭包在这些语言中也不是原生概念)。

它们使您更容易遵守 Greenspun 的第十条编程规则。 (/我等着有人意识到有保证的尾调用优化也有多么有用)。

【讨论】:

  • 那么...如何为我的 UIView 设置动画?对不起,这对我来说太理论化了。我没有参加任何计算机编程课程,因为我的大学要求在他们的入门课程中具备 Python 的先验知识(我当时不知道,后来一瘸一拐地通过了)。
【解决方案3】:

如果您还没有阅读过 Apple 的 View Programming Guide,那么您真的应该阅读。尤其是section on Animations

这是直接来自该文档的示例代码:

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

您看到带有完成块的部分吗?他们声明/创建与调用函数内联的块。当您第一次开始时,您应该始终查看 Apple 的代码并尽可能密切地关注它们。当您获得更多经验时,您可以扩展并尝试其他方法。

【讨论】:

    【解决方案4】:

    大家好:我今天看到了 StackOverflow 帖子 Best Way to Perform Several Sequential UIView Animations?,其中包括用户 Yang 使用 CPAnimationSequence 的回答!链接如下。

    这看起来很棒!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-12-15
      • 1970-01-01
      • 1970-01-01
      • 2011-03-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多