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