Pal*_*ndo 6 core-animation uiview objective-c-blocks
自从他们在iOS 4中推出以来,我一直想知道UIView基于块的动画方法的内部实现.特别是我想了解在那里使用Objective C的神秘特征来捕获动画块执行之前和之后的所有相关层状态变化.
观察黑盒实现,我认为它需要捕获动画块中修改的所有图层属性的前置状态,以创建所有相关的CAAnimations.我想它不会对整个视图层次结构进行快照,因为这样会非常低效.动画块在运行时是不透明的代码blob,所以我认为它不能直接分析它.它是否CALayer用某种重新编码版本取代了属性设置器的实现?或者是对这个属性的支持是否会改变在内部深处的某些内容CALayers?
为了概括一下这个问题,是否可以创建类似的基于块的API,用于使用某些Objective C暗魔法记录状态变化,或者这是否依赖于知道并且可以访问块中更改的对象的内部?
Dav*_*ist 16
它实际上是一个非常优雅的解决方案,它围绕视图是层委托这一事实而构建,并且独立层隐式地对属性更改进行动画处理.
恰好是几天前我在NSConference上给了BLITZ谈论这个问题,我在GitHub上发布了我的幻灯片,试图写下我在演示者笔记中说的或多或少的内容.
那说:这是一个非常有趣的问题,我不经常被问到.它可能有点广泛,但我真的很喜欢好奇心.
自从他们在iOS 4中推出以来,我一直在想UIView的基于块的动画方法的内部实现.
UIView动画在iOS 4之前存在,但是以不同的风格(不再推荐使用,因为它使用起来比较麻烦).例如,可以像这样完成具有延迟的视图的动画位置和颜色.免责声明:我没有运行此代码,因此它可能包含错误.
// 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];
Run Code Online (Sandbox Code Playgroud)
特别是我想了解在那里使用Objective C的神秘特征来捕获动画块执行之前和之后的所有相关层状态变化.
实际上是另一种方式.视图构建在图层的顶部,它们可以非常紧密地协同工作.在视图上设置属性时,它会在图层上设置相应的属性.例如,您可以看到视图甚至没有框架,边界或位置的自己的变量.
观察黑盒实现,我认为它需要捕获动画块中修改的所有图层属性的前置状态,以创建所有相关的CAAnimations.
它不需要这样做,这是一切都非常优雅的地方.每当图层属性发生更改时,图层都会查找要执行的操作(动画的更通用术语).由于在视图上设置大多数属性实际上是在图层上设置属性,因此您隐式设置了一堆图层属性.
图层寻找操作的第一个位置是它询问图层委托(记录的行为是视图是图层委托).这意味着当图层属性更改时,图层会要求视图为每个属性更改提供动画对象.因此视图不需要跟踪任何状态,因为图层具有状态,并且图层要求视图在属性更改时提供动画.
实际上,这并不完全正确.视图需要跟踪某些状态,例如:如果您在块内,是否使用了动画的持续时间等.
您可以想象API看起来像这样.
注意:我不知道实际的实现是什么,这显然是一个很大的简化来证明一点
// 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;
}
Run Code Online (Sandbox Code Playgroud)
它是否用某种重新编码版本替换了CALayer上的属性设置器的实现?
不(见上文)
或者是对这个属性的支持是否会改变在CALayers内部的某处进行重新编码?
是的,有点(见上文)
为了概括一下这个问题,是否可以创建类似的基于块的API,用于使用某些Objective C暗魔法记录状态变化,或者这是否依赖于知道并且可以访问块中更改的对象的内部?
如果您想根据属性更改提供自己的动画,则可以创建类似的基于块的API.如果你看一下我在NSConference的演讲中展示的用于检查UIView动画的技术(直接询问图层actionForLayer:forKey:并使用layerClass创建记录所有addAnimation:forKey:信息的图层),那么你应该能够充分了解视图如何使用用于创建此抽象的图层.
我不确定录制状态的改变是否是你的最终目标.如果您只想做自己的动画API,那么您不应该这样做.如果你真的想这样做,你可能会这样做,但是没有像动画那样可用的通信基础设施(视图和图层之间的委托方法和回调).
大卫的回答很棒。你应该接受它作为最终答案。
我确实有一点贡献。我在我的一个名为“ Sleuthing UIView Animations ”的github 项目中创建了一个 Markdown 文件。(链接)它更详细地介绍了如何观看系统为响应 UIView 动画而创建的 CAAnimation 对象。该项目称为KeyframeViewAnimations。(关联)
它还显示了记录在您提交 UIView 动画时创建的 CAAnimations 的工作代码。
而且,为了在应得的地方给予赞扬,大卫建议了我使用的技术。