UIView基于块的动画方法的内部实现 [英] Internal implementation of UIView's block-based animation methods

查看:81
本文介绍了UIView基于块的动画方法的内部实现的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

自从它们在iOS 4中引入以来,我一直想知道UIView基于块的动画方法的内部实现.特别是,我想了解在执行动画块之前和之后,Objective C的哪些神秘功能用于捕获所有相关的层状态变化.

Ever since their introduction in iOS 4, I have been wondering about the internal implementation of the UIView's block-based animation methods. In particular I would like to understand what mystical features of Objective C are used there to capture all the relevant layer state changes before and after execution of the animation block.

观察黑盒实现,我收集到它需要捕获在动画块中修改的所有图层属性的前态,以创建所有相关的CAAnimations.我猜它不能对整个视图层次进行快照,因为那样效率太低了.动画块在运行时是不透明的代码blob,因此我认为它不能直接对其进行分析.它是否用某种重新编码版本代替了CALayer上属性设置器的实现?还是在CALayers内部深处的某个地方进行了对此属性更改重新编码的支持?

Observing the black-box implementation, I gather that it needs to capture the before-state of all layer properties modified in the animation block, to create all the relevant CAAnimations. I guess it does not do a snapshot of whole view hierarchy, as that would be horribly inefficient. The animation block is opaque code blob during runtime, so I don't think it can analyze that directly. Does it replace the implementation of property setters on CALayer with some kind of recoding versions? Or is the support for this property change recoding baked-in somewhere deep inside the CALayers?

稍微概括一下这个问题,是否有可能使用一些Objective C黑暗魔术来创建类似的基于块的API来记录状态变化,还是依赖于了解并拥有对被更改对象内部的访问权限?在街区?

To generalize the question a little bit, is it possible do create similar block-based API for recording state changes using some Objective C dark magic, or does this rely on knowing and having the access to the internals of the objects being changed in the block?

推荐答案

这实际上是一个非常优雅的解决方案,它基于以下事实构建:视图是图层的委托,而视图是独立的图层隐式地 do 为属性更改设置动画.

It is actually a very elegant solution that is built around the fact that the view is the layers delegate and that stand-alone layers implicitly do animate on property changes.

恰好是几天前,我在NSConference上对BLITZ进行了讨论,并发布了

It just happens to be that I gave a BLITZ talk about this at NSConference just a couple of days ago and I posted my slides on GitHub and tried to write down more or less what I said in the presenter notes.

那是一个非常有趣的问题,我不经常被问到.可能有点宽泛,但是我真的很好奇.

That said: it is a very interesting question that I don't see being asked very often. It may be a bit to broad but I really like curiosity.

自从它们在iOS 4中引入以来,我一直想知道UIView基于块的动画方法的内部实现.

Ever since their introduction in iOS 4, I have been wondering about the internal implementation of the UIView's block-based animation methods.

UIView动画在iOS 4之前就存在,但是采用了不同的样式(不再建议使用,因为使用起来比较麻烦).例如,可以像这样对视图的位置和颜色进行延迟动画处理. 免责声明:我没有运行此代码,因此其中可能包含错误.

UIView animations existed before iOS 4 but in a different style (that is no longer recommended to use because it is more cumbersome to use). For example, animating position and color of a view with a delay could be done like this. Disclaimer: I did not run this code so it may contains bugs.

// Setup
static void *myAnimationContext = &myAnimationContext;
[UIView beginAnimations:@"My Animation ID" context:myAnimationContext];
// Configure
[UIView setAnimationDuration:1.0];
[UIView setAnimationDelay:0.25];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];

// Make changes
myView.center = newCenter;
myView.backgroundColor = newColor;

// Commit
[UIView commitAnimations];

视图层的协同作用非常优雅

我特别想了解在其中使用Objective C的哪些神秘功能来捕获动画块执行前后所有相关的层状态变化.

In particular I would like to understand what mystical features of Objective C are used there to capture all the relevant layer state changes before and after execution of the animation block.

实际上是另一回事.视图建立在该层的顶部,并且它们非常紧密地协同工作.在视图上设置属性时,它会在图层上设置相应的属性.例如,您可以看到视图甚至没有框架的自身变量,即边界或位置.

It is actually the other way around. The view is built on top of the layer and they work together very closely. When you set a property on the view it sets the corresponding property on the layer. You can for example see that the view doesn't even have it's own variable for the frame, bounds or position.

观察黑盒实现,我认为它需要捕获在动画块中修改的所有图层属性的前状态,以创建所有相关的CAAnimations.

Observing the black-box implementation, I gather that it needs to capture the before-state of all layer properties modified in the animation block, to create all the relevant CAAnimations.

它不需要这样做,而 this 可以使一切变得非常优雅.每当图层属性更改时,图层就会寻找要执行的动作(动画的更通用的术语).由于在视图上设置大多数属性实际上是在图层上设置属性,因此您隐式设置了一堆图层属性.

It does not need to do that and this is where it all gets very elegant. Whenever a layer property changes, the layer looks for the action (a more general term for an animation) to execute. Since setting most properties on a view actually sets the property on the layer, you are implicitly setting a bunch of layer properties.

该层寻找动作的第一处是它询问该层委托(该视图行为是该层委托已被记录为行为).这意味着,当图层属性更改时,图层会要求视图为每个属性更改提供一个动画对象.因此,该视图不需要跟踪任何状态,因为该层具有状态,并且该层要求该视图在属性更改时 提供动画.

The first place that the layer goes looking for an action is that it asks the layer delegate (it is documented behaviour that the view is the layers delegate). This means that when the layer property changes, the layers asks the view to provide an animation object for that each property change. So the view doesn't need to keep track of any state since the layer has the state and the layer asks the view to provide an animation when the properties change.

实际上,这并非完全正确.视图需要跟踪 some 状态,例如:如果是否在块内,动画使用的持续时间等.

Actually, that's not entirely true. The view needs to keep track of some state such as: if you are inside of the block or not, what duration to use for the animation, etc.

您可以想象API看起来像这样.

You could imagine that the API looks something like this.

注意:我不知道实际的实现是什么,这显然是巨大的简化,以证明这一点

Note: I don't know what the actual implementation does and this is obviously a huge simplification to prove a point

// static variables since this is a class method
static NSTimeInterval _durationToUseWhenAsked;
static BOOL _isInsideAnimationBlock;

// Oversimplified example implementation of how it _could_ be done
+ (void)animateWithDuration:(NSTimeInterval)duration
                 animations:(void (^)(void))animations
{
    _durationToUseWhenAsked = duration;
    _isInsideAnimationBlock = YES;
    animations();
    _isInsideAnimationBlock = NO;
}

// Running the animations block is going to change a bunch of properties
// which result in the delegate method being called for each property change
- (id<CAAction>)actionForLayer:(CALayer *)layer
                        forKey:(NSString *)event
{
    // Don't animate outside of an animation block
    if (!_isInsideAnimationBlock)
        return (id)[NSNull null]; // return NSNull to don't animate

    // Only animate certain properties
    if (![[[self class] arrayOfPropertiesThatSupportAnimations] containsObject:event])
        return (id)[NSNull null]; // return NSNull to don't animate

    CABasicAnimation *theAnimation = [CABasicAnimation animationWithKeyPath:event];
    theAnimation.duration = _durationToUseWhenAsked;

    // Get the value that is currently seen on screen
    id oldValue = [[layer presentationLayer] valueForKeyPath:event];
    theAnimation.fromValue = oldValue;
    // Only setting the from value means animating form that value to the model value

    return theAnimation;
}

是否用某种重新编码版本代替CALayer上属性设置器的实现?

Does it replace the implementation of property setters on CALayer with some kind of recoding versions?

否(如上所述)

或者是否支持此属性更改重新编码在CALayers内部的某个地方?

Or is the support for this property change recoding baked-in somewhere deep inside the CALayers?

是的,(见上文)

稍微概括一下这个问题,是否有可能使用一些Objective C黑暗魔术来创建类似的基于块的API来记录状态变化,还是依赖于了解并拥有对被更改对象内部的访问权限?在街区?

To generalize the question a little bit, is it possible do create similar block-based API for recording state changes using some Objective C dark magic, or does this rely on knowing and having the access to the internals of the objects being changed in the block?

如果您要基于属性更改提供自己的动画,则可以绝对创建类似的基于块的API.如果您看一下我在NSConference演讲中展示的检查UIView动画的技术(直接向图层查询actionForLayer:forKey:并使用layerClass创建一个记录所有addAnimation:forKey:信息的图层),那么您应该能够了解有关视图如何使用图层创建此抽象的知识.

You can definitely create a similar block based API if you want to provide your own animations based on property changes. If you look at the techniques I showed in my talk at NSConference for inspecting UIView animations (directly asking the layer for the actionForLayer:forKey: and using layerClass to create a layer that logs all addAnimation:forKey: information) then you should be able to learn enough about how the view is using the layer to create this abstraction.

我不确定记录状态更改是否是您的最终目标.如果您只想创建自己的动画API,则不必这样做.如果您真的想这样做,也许可以,但是不会有像动画那样可用的通信基础结构(视图和图层之间的委托方法和回调).

I'm not sure if recording state changes is you end goal or not. If you only want to do your own animation API then you shouldn't have to. If you really want to do it, You could probably could, but there wouldn't be as much communication infrastructure (delegate methods and callbacks between the view and the layer) available to you as there is for animations.

这篇关于UIView基于块的动画方法的内部实现的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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