仿映客刷禮物效果---代碼優(yōu)化

上一篇文章《仿映客刷禮物效果---基本邏輯實現(xiàn)》中,分析了刷禮物效果的基本流程與具體實現(xiàn)代碼。但還有一些BUG和一些可優(yōu)化的地方?jīng)]有處理,現(xiàn)在我們就來分析下這些遺留的問題。當然個人的能力是有限的,肯定還有很多我沒有發(fā)現(xiàn)到的問題,如果大家在使用過程中有遇到其它的問題,歡迎大家及時指出,我好及時完善。

優(yōu)化后的效果圖如下:


效果圖.png

廢話不多說,先看問題。

問題一

問題描述

問題效果圖如下圖:


問題一.gif

從圖中可以看到:當一個禮物動畫組正在執(zhí)行隱藏動畫時,這時恰好收到一個新的與之相同類型的禮物消息,按正常邏輯來看,這個新的禮物消息應該應該作為一個新的動畫組開始展示。但是,從GIF圖中可以看到,連送按鈕再次出現(xiàn)的時,新的動畫組并沒有開始展示,那新接收到禮物消息去哪里了呢?

問題原因

如果將隱藏動畫的動畫時間設置長一點,重復上面的問題流程。你就會發(fā)現(xiàn),其實新接收到的禮物消息并不是消失了,而是被判定為一次連乘動畫。所以這時其實是一邊執(zhí)行隱藏動畫,一遍執(zhí)行連乘動畫,這就導致連乘動畫很難被看到,從而造成了新接收到的禮物消息消失了。

問題解決

知道了問題原因,解決問題就非常簡單了。這里我的解決方法是:讓cell在執(zhí)行隱藏動畫時不被判定為正在執(zhí)行動畫,因此我給cell設置了幾種動畫狀態(tài),其邏輯關系如下圖所示:


動畫狀態(tài)邏輯.png

有了動畫狀態(tài)之后,只用修改動畫檢測的判斷條件就可以了,修改后的代碼如下:

- (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可以執(zhí)行連乘動畫。
運行程序,再次測試,就會發(fā)現(xiàn)當cell正在執(zhí)行隱藏動畫時收到一條相同類型消息,新的消息會在新的動畫組中展示,或是等有了空閑的cell時在展示。(修改后的具體效果可以在Demo中驗證,下同)

問題二

問題描述

問題效果圖如下:


問題二.gif

從圖中可以看到:當連續(xù)多次快速的點擊同一個發(fā)送按鈕時,連乘的動畫效果就消失了。

問題原因

很明顯這里的問題原因就是因為:上一次點擊的連乘動畫還沒執(zhí)行完,就開始了下一次連乘動畫,從而造成了這種效果。

問題解決

從問題原因中很容易想到這里需要用到緩存機制,即等到上一次動畫執(zhí)行完了再執(zhí)行下一次動畫。首先我們很容易想到的就是NSOperationQueue和dispatch_group_t,這是系統(tǒng)封裝的兩個任務隊列,很容易實現(xiàn)上面的需求,而且還特別簡單,只用在shakeAnimationWithNumber:里面實現(xiàn)緩存機制就行了。這里介紹下dispatch_group_t隊列的實現(xiàn)方法,其代碼如下:

- (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];
    });
}

代碼很簡單,就是創(chuàng)建一個全局的串行隊列,如果group任務完成就延時執(zhí)行隱藏動畫,每次調(diào)用都想group中添加一個連乘動畫任務。
運行程序,你會發(fā)現(xiàn)問題還是存在,這是為什么呢?其實原因很簡單,就是因為:UIView封裝的動畫是在子線程中執(zhí)行,與添加任務操作不在同一個線程中。所以雖然任務是順序添加的,但動畫的執(zhí)行并不是串行執(zhí)行的。
這里的解決辦法是:自己實現(xiàn)緩存--收到連乘動畫先緩存,如果有緩存并且沒有正在執(zhí)行連乘動畫,就取緩存,開始動畫,動畫完成就刪除緩存,再去取緩存,只到?jīng)]有緩存為止。具體實現(xiàn)代碼如下:

- (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是為了緩存不被重復添加
        }];
    }
}

再次運行程序,就會發(fā)現(xiàn)連乘動畫是串行執(zhí)行了。

問題三

問題描述

在開始下一次動畫去緩存時,這時剛好收到一個與取出的緩存相同類型的消息,又會去取緩存。在極端的情況下,這會造成兩個相同類型的禮物動畫同時展示。
因為這是一個邏輯上特別極端的情況,所以這里沒有給出效果圖。

問題原因

這個問題原因其實就是因為:取緩存到動畫開始這段時間內(nèi)收到一條與取出緩存相同類型的消息,導致這個新的消息在做動畫檢測時沒有檢測出來,所以新的消息可能也會作為新的動畫組開始展示。

問題解決

這里的解決辦法是將取緩存到動畫開始這個時間縮減到最短,也就是在開始展示動畫前就將cell的動畫狀態(tài)從AnimationStateNone設置成AnimationStateShowing。
關于這個問題,有興趣的可以自行測試,測試時記得要增大取緩存到開始動畫這段時間。具體操作就是:增大cell的顯示動畫時間,并且等cell的顯示動畫完成再將cell的動畫狀態(tài)從AnimationStateNone設置成AnimationStateShowing。(如果有測試出問題的,記得將問題效果的gif圖分享給我!拜謝了?。?/p>

問題四

解決一個問題,就會帶來新的問題。這里給動畫做了這么多的緩存之后帶來的新的問題是什么呢!想必測試過的應該已經(jīng)發(fā)現(xiàn)了:連送按鈕已經(jīng)隱藏了,但是連乘動畫還在執(zhí)行,這肯定是不合邏輯的。

對于這個問題,只有在項目中有要求出現(xiàn)連送按鈕才會出現(xiàn),而且出現(xiàn)了也不一定算是問題(映客也沒解決這個問題),所以這里就不解決這個問題了,如果確實有要求的,這里提供下思路:因為展示動畫和連乘動畫的動畫時間都是確定的,所以很容易計算出執(zhí)行完當前動畫組需要的時間。然后每接收到一個連乘動畫就對這個時間進行更新,并用代理或其它的形式將這個時間值傳遞出去,最后根據(jù)這個值對連送按鈕進行控制就可以了。(當然了,有實現(xiàn)了這個思路或是有更好思路的,方便的話,也請分享給我,再次拜謝了?。?/p>

一個好的功能應該除了具有易用性之外,還應該具有良好的擴展性。

擴展

因為項目的需求不同,所以對展示動畫與隱藏動畫的要求也各部相同。這里就提供了對這連個動畫自定義的接口,接口名如下:

/**
 *  自定義展示動畫
 *
 *  @param flag 是否帶有連乘動畫
 */
- (void)customDisplayAnimationOfShowShakeAnimation:(BOOL)flag;
/**
 *  自定義隱藏動畫
 *
 *  @param flag 是否帶有連乘動畫
 */
- (void)customHideAnimationOfShowShakeAnimation:(BOOL)flag;

如果想對這兩動畫進行簡單的自定義,就可以在自定義cell中重寫這兩方法。需要注意的是,這兩方法是在UIView封裝的動畫的animations回調(diào)中調(diào)用,所以只用修改cell的frame就夠了。

一般項目中通過這種方法自定義cell的展示和隱藏動畫就可以滿足需求,如果項目中確實需要更絢麗的動畫效果,就需要修改PrentViewCell的showAnimationWithModel:showShakeAnimation:prepare:completion:方法和hiddenAnimationOfShowShake:方法中的動畫代碼。

當然,還有其它的地方可以擴展,例如:讓禮物消息只在指定的cell上展示,如VIP用戶發(fā)送的禮物消息,在特定的位置上展示;還可以對消息的展示優(yōu)先級進行區(qū)分,如不同等級的用戶展示消息的優(yōu)先級就不一樣,等等這些都是有可能項目需要的需求。這里就不一一對這些需求進行實現(xiàn)了,關于優(yōu)化后的Demo,大家可以點擊這里下載。

最后,還是開篇那句話:個人的能力有限,肯定還有很多我沒有發(fā)現(xiàn)到的問題,如果大家在使用過程中有遇到其它的問題,歡迎大家及時指出,我好及時完善。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

  • 最近做了個直播項目,需要用到彈幕和刷禮物。在網(wǎng)上找了許多開源代碼,發(fā)現(xiàn)都不是很適合自己的項目需求,于是利用空余時間...
    Rasping閱讀 4,916評論 23 40
  • 發(fā)現(xiàn) 關注 消息 iOS 第三方庫、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關注 2017.06.26 09:4...
    肇東周閱讀 15,043評論 4 61
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 178,745評論 25 709
  • 記憶已經(jīng)模糊了太多太多,因為想不起的東西,就那么一直被遺忘了,故事的開始和結(jié)束都不再是故事的重點了,享受的只是過程...
    一空一白閱讀 147評論 0 0

友情鏈接更多精彩內(nèi)容