最近做一个下拉刷新的需求,踩到一个坑,如下:
self.animView = [[LOAAnimationView alloc] initWithName:@"demo"];
self.animView.frame = self.view.bounds;
self.animView.center = self.view.center;
[self.view addSubview:self.animView];
self.animView.animationProgress = 0.4;
有兴趣可以试一试这段代码,重点在于最后一行 self.animView.animationProgress = 0.4 这句。
在addSubview的同时,设置动画的进度。然后后面的某个时刻再设置animationProgress,你会发现动画显示的进度和你预期的不一样。
比如这段代码,设置animationProgress为0.4。那么后面再设置任何比0.4小的progress时animView不会有人和变化,而设置大于0.4时,界面才开始变化,而实际显示的进度是你设置的progress-0.4.如此就会导致即使你设置progress=1,动画也不会播放到最后一帧,而是播放到0.6也就是60%的进度。于是我经过一系列的debug,发现了问题。
当animView被添加到屏幕并且没有被渲染之前,任意修改动画图层(父图层)的进度都会改变子图层(lottie的每个图层都有一个inout动画及LottieAnimation动画)的begintime,因此导致在后面设置进度时和实际传值显示的进度不一致。
避免这一iOS内部逻辑的方法有2个
- 在addSubview的同时,即在前后两个渲染帧之间不要设置progress。基本上addSubview时不要设置进度就ok,除少数极端情况。
- 修改源码的setNeedsAnimationUpdate方法,把下一个runloop重置状态改为下一帧渲染后重置状态。并且对上屏进行判断,再上屏之前进掉进度设置。但为了尽量少改动源码,所以就直接延时了一帧的时间再进行原来的操作。
最后,iOS内部实现为什么把设置timeoffset的值给了子图层的begintime我还不是很清楚,如果你知道欢迎来指教~~~
最终代码修改可以查看https://github.com/juxuechen/lottie-ios/tree/1.5.1
后面这个bug提了pr,也就是上面的链接。当时更新到了1.5.2版本。1.5.2的源码我看也对这个bug有尝试修复,但是只考虑到了上屏这里,并未修复这个bug。
于是又提了issue,作者回复会在2.0修复。在Lottie2.0版本中,他们修改了整个render逻辑,动画的播放不再依赖修改图层的begintime等基础属性,而是通过add一个baseAnimation来实现play,绕开了底层的属性设置,播放完全由系统来处理。虽然第一次启动时下拉也会有断帧情况,这是因为启动时一个runllop里面的操作太多了,滑动的runloop被回调的间隔也变长。但是bug同样修复了。