带块的UIView动画如何在后台运行 [英] How UIView animations with blocks work under the hood

查看:195
本文介绍了带块的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.

说我有这样的代码:

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

作为animations参数传递的东西是一个Objective-C块(类似于lambdas/其他语言的匿名函数),可以执行该块,然后将其从当前更改为labelalpha属性.值设为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.

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

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的工作原理,您可能需要阅读

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天全站免登陆