最近做一個下拉刷新的需求,踩到一個坑,如下:
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的同時,設(shè)置動畫的進度。然后后面的某個時刻再設(shè)置animationProgress,你會發(fā)現(xiàn)動畫顯示的進度和你預(yù)期的不一樣。
比如這段代碼,設(shè)置animationProgress為0.4。那么后面再設(shè)置任何比0.4小的progress時animView不會有人和變化,而設(shè)置大于0.4時,界面才開始變化,而實際顯示的進度是你設(shè)置的progress-0.4.如此就會導(dǎo)致即使你設(shè)置progress=1,動畫也不會播放到最后一幀,而是播放到0.6也就是60%的進度。于是我經(jīng)過一系列的debug,發(fā)現(xiàn)了問題。
當(dāng)animView被添加到屏幕并且沒有被渲染之前,任意修改動畫圖層(父圖層)的進度都會改變子圖層(lottie的每個圖層都有一個inout動畫及LottieAnimation動畫)的begintime,因此導(dǎo)致在后面設(shè)置進度時和實際傳值顯示的進度不一致。
避免這一iOS內(nèi)部邏輯的方法有2個
- 在addSubview的同時,即在前后兩個渲染幀之間不要設(shè)置progress?;旧蟖ddSubview時不要設(shè)置進度就ok,除少數(shù)極端情況。
- 修改源碼的setNeedsAnimationUpdate方法,把下一個runloop重置狀態(tài)改為下一幀渲染后重置狀態(tài)。并且對上屏進行判斷,再上屏之前進掉進度設(shè)置。但為了盡量少改動源碼,所以就直接延時了一幀的時間再進行原來的操作。
最后,iOS內(nèi)部實現(xiàn)為什么把設(shè)置timeoffset的值給了子圖層的begintime我還不是很清楚,如果你知道歡迎來指教~~~
最終代碼修改可以查看https://github.com/juxuechen/lottie-ios/tree/1.5.1
后面這個bug提了pr,也就是上面的鏈接。當(dāng)時更新到了1.5.2版本。1.5.2的源碼我看也對這個bug有嘗試修復(fù),但是只考慮到了上屏這里,并未修復(fù)這個bug。
于是又提了issue,作者回復(fù)會在2.0修復(fù)。在Lottie2.0版本中,他們修改了整個render邏輯,動畫的播放不再依賴修改圖層的begintime等基礎(chǔ)屬性,而是通過add一個baseAnimation來實現(xiàn)play,繞開了底層的屬性設(shè)置,播放完全由系統(tǒng)來處理。雖然第一次啟動時下拉也會有斷幀情況,這是因為啟動時一個runllop里面的操作太多了,滑動的runloop被回調(diào)的間隔也變長。但是bug同樣修復(fù)了。