上一篇文章《仿映客刷禮物效果---基本邏輯實現》中,分析了刷禮物效果的基本流程與具體實現代碼。但還有一些BUG和一些可優化的地方沒有處理,現在我們就來分析下這些遺留的問題。當然個人的能力是有限的,肯定還有很多我沒有發現到的問題,如果大家在使用過程中有遇到其它的問題,歡迎大家及時指出,我好及時完善。
優化后的效果圖如下:
廢話不多說,先看問題。
問題一
問題描述
問題效果圖如下圖:
從圖中可以看到:當一個禮物動畫組正在執行隱藏動畫時,這時恰好收到一個新的與之相同類型的禮物消息,按正常邏輯來看,這個新的禮物消息應該應該作為一個新的動畫組開始展示。但是,從GIF圖中可以看到,連送按鈕再次出現的時,新的動畫組并沒有開始展示,那新接收到禮物消息去哪里了呢?
問題原因
如果將隱藏動畫的動畫時間設置長一點,重復上面的問題流程。你就會發現,其實新接收到的禮物消息并不是消失了,而是被判定為一次連乘動畫。所以這時其實是一邊執行隱藏動畫,一遍執行連乘動畫,這就導致連乘動畫很難被看到,從而造成了新接收到的禮物消息消失了。
問題解決
知道了問題原因,解決問題就非常簡單了。這里我的解決方法是:讓cell在執行隱藏動畫時不被判定為正在執行動畫,因此我給cell設置了幾種動畫狀態,其邏輯關系如下圖所示:
有了動畫狀態之后,只用修改動畫檢測的判斷條件就可以了,修改后的代碼如下:
- (PresentViewCell *)examinePresentingCell:(id<PresentModelAble>)obj
{
for (PresentViewCell *cell in self.showCells) {
if ([cell.sender isEqualToString:[obj sender]] && [cell.giftName isEqualToString:[obj giftName]]) {
//當前正在展示動畫并且不是隱藏動畫
if (cell.state != AnimationStateNone && cell.state != AnimationStateHiding) return cell;
}
}
return nil;
}
如果當前沒有不為空閑并且也沒有在隱藏動畫,就判定當前cell可以執行連乘動畫。
運行程序,再次測試,就會發現當cell正在執行隱藏動畫時收到一條相同類型消息,新的消息會在新的動畫組中展示,或是等有了空閑的cell時在展示。(修改后的具體效果可以在Demo中驗證,下同)
問題二
問題描述
問題效果圖如下:
從圖中可以看到:當連續多次快速的點擊同一個發送按鈕時,連乘的動畫效果就消失了。
問題原因
很明顯這里的問題原因就是因為:上一次點擊的連乘動畫還沒執行完,就開始了下一次連乘動畫,從而造成了這種效果。
問題解決
從問題原因中很容易想到這里需要用到緩存機制,即等到上一次動畫執行完了再執行下一次動畫。首先我們很容易想到的就是NSOperationQueue和dispatch_group_t,這是系統封裝的兩個任務隊列,很容易實現上面的需求,而且還特別簡單,只用在shakeAnimationWithNumber:里面實現緩存機制就行了。這里介紹下dispatch_group_t隊列的實現方法,其代碼如下:
- (void)shakeAnimationWithNumber:(NSInteger)number
{
if (!_queue && !_group) {
_queue = dispatch_queue_create("com.shakeCache.queue", DISPATCH_QUEUE_SERIAL);
_group = dispatch_group_create();
dispatch_group_notify(_group, dispatch_get_main_queue(), ^{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self hiddenAnimationOfShowShake:YES];
});
});
}
dispatch_group_async(_group, _queue, ^{
[self startShakeAnimationWithNumber:number completion:nil];
});
}
代碼很簡單,就是創建一個全局的串行隊列,如果group任務完成就延時執行隱藏動畫,每次調用都想group中添加一個連乘動畫任務。
運行程序,你會發現問題還是存在,這是為什么呢?其實原因很簡單,就是因為:UIView封裝的動畫是在子線程中執行,與添加任務操作不在同一個線程中。所以雖然任務是順序添加的,但動畫的執行并不是串行執行的。
這里的解決辦法是:自己實現緩存--收到連乘動畫先緩存,如果有緩存并且沒有正在執行連乘動畫,就取緩存,開始動畫,動畫完成就刪除緩存,再去取緩存,只到沒有緩存為止。具體實現代碼如下:
- (void)shakeAnimationWithNumber:(NSInteger)number
{
if (number > 0) [self.caches addObject:@(number)];
if (self.caches.count > 0 && _state != AnimationStateShaking) {
NSInteger cache = [self.caches.firstObject integerValue];
[self.caches removeObjectAtIndex:0];//不能刪除對象,因為可能有相同的對象
__weak typeof(self) ws = self;
[self startShakeAnimationWithNumber:cache completion:^(BOOL finished) {
[ws shakeAnimationWithNumber:-1];//傳-1是為了緩存不被重復添加
}];
}
}
再次運行程序,就會發現連乘動畫是串行執行了。
問題三
問題描述
在開始下一次動畫去緩存時,這時剛好收到一個與取出的緩存相同類型的消息,又會去取緩存。在極端的情況下,這會造成兩個相同類型的禮物動畫同時展示。
因為這是一個邏輯上特別極端的情況,所以這里沒有給出效果圖。
問題原因
這個問題原因其實就是因為:取緩存到動畫開始這段時間內收到一條與取出緩存相同類型的消息,導致這個新的消息在做動畫檢測時沒有檢測出來,所以新的消息可能也會作為新的動畫組開始展示。
問題解決
這里的解決辦法是將取緩存到動畫開始這個時間縮減到最短,也就是在開始展示動畫前就將cell的動畫狀態從AnimationStateNone設置成AnimationStateShowing。
關于這個問題,有興趣的可以自行測試,測試時記得要增大取緩存到開始動畫這段時間。具體操作就是:增大cell的顯示動畫時間,并且等cell的顯示動畫完成再將cell的動畫狀態從AnimationStateNone設置成AnimationStateShowing。(如果有測試出問題的,記得將問題效果的gif圖分享給我!拜謝了!)
問題四
解決一個問題,就會帶來新的問題。這里給動畫做了這么多的緩存之后帶來的新的問題是什么呢!想必測試過的應該已經發現了:連送按鈕已經隱藏了,但是連乘動畫還在執行,這肯定是不合邏輯的。
對于這個問題,只有在項目中有要求出現連送按鈕才會出現,而且出現了也不一定算是問題(映客也沒解決這個問題),所以這里就不解決這個問題了,如果確實有要求的,這里提供下思路:因為展示動畫和連乘動畫的動畫時間都是確定的,所以很容易計算出執行完當前動畫組需要的時間。然后每接收到一個連乘動畫就對這個時間進行更新,并用代理或其它的形式將這個時間值傳遞出去,最后根據這個值對連送按鈕進行控制就可以了。(當然了,有實現了這個思路或是有更好思路的,方便的話,也請分享給我,再次拜謝了!)
一個好的功能應該除了具有易用性之外,還應該具有良好的擴展性。
擴展
因為項目的需求不同,所以對展示動畫與隱藏動畫的要求也各部相同。這里就提供了對這連個動畫自定義的接口,接口名如下:
/**
* 自定義展示動畫
*
* @param flag 是否帶有連乘動畫
*/
- (void)customDisplayAnimationOfShowShakeAnimation:(BOOL)flag;
/**
* 自定義隱藏動畫
*
* @param flag 是否帶有連乘動畫
*/
- (void)customHideAnimationOfShowShakeAnimation:(BOOL)flag;
如果想對這兩動畫進行簡單的自定義,就可以在自定義cell中重寫這兩方法。需要注意的是,這兩方法是在UIView封裝的動畫的animations回調中調用,所以只用修改cell的frame就夠了。
一般項目中通過這種方法自定義cell的展示和隱藏動畫就可以滿足需求,如果項目中確實需要更絢麗的動畫效果,就需要修改PrentViewCell的showAnimationWithModel:showShakeAnimation:prepare:completion:方法和hiddenAnimationOfShowShake:方法中的動畫代碼。
當然,還有其它的地方可以擴展,例如:讓禮物消息只在指定的cell上展示,如VIP用戶發送的禮物消息,在特定的位置上展示;還可以對消息的展示優先級進行區分,如不同等級的用戶展示消息的優先級就不一樣,等等這些都是有可能項目需要的需求。這里就不一一對這些需求進行實現了,關于優化后的Demo,大家可以點擊這里下載。
最后,還是開篇那句話:個人的能力有限,肯定還有很多我沒有發現到的問題,如果大家在使用過程中有遇到其它的問題,歡迎大家及時指出,我好及時完善。