带有块的 UIView 动画如何在引擎盖下工作 [英] How UIView animations with blocks work under the hood

查看:14
本文介绍了带有块的 UIView 动画如何在引擎盖下工作的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是 Objective-C/iOS 编程的新手,我正在尝试了解 UIView 动画是如何在幕后工作的.

I'm new to Objective-C/iOS programming and I'm trying to understand how UIView animation works under the hood.

假设我有一个这样的代码:

Say I have a code like this:

[UIView animateWithDuration:2.0 animations:^{
    self.label.alpha = 1.0;
}];

作为 animations 参数传递的东西是一个 Objective-C 块(类似于其他语言中的 lambdas/匿名函数),它可以被执行,然后它会改变 alpha<label 的/code> 属性从当前值到 1.0.

The thing that gets passed as an animations argument is an Objective-C block (something like lambdas/anonymous functions in other languages) that can be executed and then it changes the alpha property of label from current value to 1.0.

然而,块不接受动画进度参数(比如从 0.0 到 1.0 或从 0 到 1000).我的问题是动画框架如何使用此块来了解中间帧,因为该块仅指定最终状态.

However, the block does not accept an animation progress argument (say going from 0.0 to 1.0 or from 0 to 1000). My question is how the animation framework uses this block to know about intermediate frames, as the block only specifies the final state.

我的问题是关于animateWithDuration 方法的幕后操作,而不是使用方法.

My questions is rather about under the hood operation of animateWithDuration method rather than the ways to use it.

我对 animateWithDuration 代码如何工作的假设如下:

My hypothesis of how animateWithDuration code works is as follows:

  1. animateWithDuration 为所有视图对象激活某种特殊状态,在这些状态中没有实际执行更改,而只是注册.
  2. 它执行块并注册更改.
  3. 它查询视图对象的更改状态并取回初始值和目标值,从而知道要更改哪些属性以及在什么范围内执行更改.
  4. 它根据持续时间和初始/目标值计算中间帧,并触发动画.
  1. animateWithDuration activates some kind of special state for all view objects in which changes are not actually performed but only registered.
  2. it executes the block and the changes are registered.
  3. it queries the views objects for changed state and gets back the initial and target values and hence knows what properties to change and in what range to perform the changes.
  4. it calculates the intermediate frames, based on the duration and initial/target values, and fires the animation.

有人可以解释一下 animateWithDuration 是否真的以这种方式工作?

Can somebody shed some light on whether animateWithDuration really works in such way?

推荐答案

当然,我不知道幕后到底发生了什么,因为 UIKit 不是开源的,而且我不在 Apple 工作,但这里有一些想法:

Of course I don't know what exactly happens under the hood because UIKit isn't open-source and I don't work at Apple, but here are some ideas:

在引入基于块的UIView动画方法之前,动画视图看起来是这样的,这些方法实际上仍然可用:

Before the block-based UIView animation methods were introduced, animating views looked like this, and those methods are actually still available:

[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:duration];
myView.center = CGPointMake(300, 300);
[UIView commitAnimations];

知道了这一点,我们可以像这样实现我们自己的基于块的动画方法:

Knowing this, we could implement our own block-based animation method like this:

+ (void)my_animateWithDuration:(NSTimeInterval)duration animations:(void (^)(void))animations
{
    [UIView beginAnimations:nil context:nil];
    [UIView setAnimationDuration:duration];
    animations();
    [UIView commitAnimations];
}

...这与现有的 animateWithDuration:animations: 方法完全相同.

...which would do exactly the same as the existing animateWithDuration:animations: method.

将块从等式中取出,很明显必须有某种全局动画状态,UIView 然后使用它在完成后对其(可动画的)属性进行动画处理在动画块中.这必须是某种堆栈,因为您可以嵌套动画块.

Taking the block out of the equation, it becomes clear that there has to be some sort of global animation state that UIView then uses to animate changes to its (animatable) properties when they're done within an animation block. This has to be some sort of stack, because you can have nested animation blocks.

实际动画由 Core Animation 执行,它在层级工作——每个 UIView 都有一个支持的 CALayer 实例,负责动画和合成,而视图大多只处理触摸事件和坐标系转换.

The actual animation is performed by Core Animation, which works at the layer level – each UIView has a backing CALayer instance that is responsible for animations and compositing, while the view mostly just handles touch events and coordinate system conversions.

我不会在这里详细介绍 Core Animation 的工作原理,您可能需要阅读 核心动画编程指南.从本质上讲,它是一个在层树中对变化进行动画处理的系统,无需明确计算每个关键帧(实际上,从 Core Animation 中获取中间值是相当困难的,您通常只需指定 from 和 to 值、持续时间等,然后让系统注意细节).

I won't go into detail here on how Core Animation works, you might want to read the Core Animation Programming Guide for that. Essentially, it's a system to animate changes in a layer tree, without explicitly calculating every keyframe (and it's actually fairly difficult to get intermediate values out of Core Animation, you usually just specify from and to values, durations, etc. and let the system take care of the details).

因为UIView是基于一个CALayer,所以它的很多属性实际上是在底层实现的.例如,当您设置或获取 view.center 时,与 view.layer.location 相同,更改其中一个也会更改另一个.

Because UIView is based on a CALayer, many of its properties are actually implemented in the underlying layer. For example, when you set or get view.center, that is the same as view.layer.location and changing either of these will also change the other.

层可以显式CAAnimation(这是一个抽象类,有许多具体的实现,比如CABasicAnimation,用于简单的事情)和 CAKeyframeAnimation 用于更复杂的东西).

Layers can be explicitly animated with CAAnimation (which is an abstract class that has a number of concrete implementations, like CABasicAnimation for simple things and CAKeyframeAnimation for more complex stuff).

那么 UIView 属性设置器可以做什么来神奇地"在动画块内完成动画更改?让我们看看我们是否可以重新实现其中之一,为简单起见,让我们使用 setCenter:.

So what might a UIView property setter do to accomplish "magically" animating changes within an animation block? Let's see if we can re-implement one of them, for simplicity's sake, let's use setCenter:.

首先,这是上面使用全局 CATransactionmy_animateWithDuration:animations: 方法的修改版本,以便我们可以在 setCenter 中找到: 方法动画需要多长时间:

First, here's a modified version of the my_animateWithDuration:animations: method from above that uses the global CATransaction, so that we can find out in our setCenter: method how long the animation is supposed to take:

- (void)my_animateWithDuration:(NSTimeInterval)duration animations:(void (^)(void))animations
{
    [CATransaction begin];
    [CATransaction setAnimationDuration:duration];

    animations();

    [CATransaction commit];
}

请注意,我们不再使用 beginAnimations:...commitAnimations,因此如果不做任何其他事情,将不会有任何动画.

Note that we don't use beginAnimations:... and commitAnimations anymore, so without doing anything else, nothing will be animated.

现在,让我们在 UIView 子类中覆盖 setCenter::

Now, let's override setCenter: in a UIView subclass:

@interface MyView : UIView
@end

@implementation MyView
- (void)setCenter:(CGPoint)position
{
    if ([CATransaction animationDuration] > 0) {
        CALayer *layer = self.layer;
        CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"];
        animation.fromValue = [layer valueForKey:@"position"];
        animation.toValue = [NSValue valueWithCGPoint:position];
        layer.position = position;
        [layer addAnimation:animation forKey:@"position"];
    }
}
@end

在这里,我们使用 Core Animation 设置了一个 explicit 动画,该动画为底层的 location 属性设置动画.动画的持续时间将自动取自 CATransaction.让我们试一试:

Here, we set up an explicit animation using Core Animation that animates the underlying layer's location property. The animation's duration will automatically be taken from the CATransaction. Let's try it out:

MyView *myView = [[MyView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
myView.backgroundColor = [UIColor redColor];
[self.view addSubview:myView];

[self my_animateWithDuration:4.0 animations:^{
    NSLog(@"center before: %@", NSStringFromCGPoint(myView.center));
    myView.center = CGPointMake(300, 300);
    NSLog(@"center after : %@", NSStringFromCGPoint(myView.center));
}];

我并不是说 UIView 动画系统就是这样工作的,只是为了展示原则上是如何工作的.

I'm not saying that this is exactly how the UIView animation system works, it's just to show how it could work in principle.

这篇关于带有块的 UIView 动画如何在引擎盖下工作的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆