iOS 基于 IM 實現仿映客刷禮物連擊效果

前段時間分享了 iOS 動畫隊列-仿映客刷禮物效果,收到不少同學的建議,大家普遍反映這個動畫在項目中是基于 IM 消息的,不能拉到項目中使用。于是我又更新了一版,便于直接引入到生產環(huán)境中使用。

Demo模擬了n個人同時送禮物

這里實現了以下功能:

  1. 隨著收到 IM 禮物消息,實時累加禮物數量

  2. 如果超出連擊時間,比如 3s ,則重新開始計數,從 1 開始

  3. 如果在連擊時間內,則在原先基礎上累加

下面把改造思路跟大家分享下:

問題一:之前動畫已經把禮物數目寫死,如何轉化為 IM 實時更新禮物數量
解決辦法 :

這里引入了一個動畫管理類 AnimOperationManager ,增加了操作緩存池和禮物緩存池,他們都用用戶聊天消息的唯一標識 userID 作為 key,這里參考了 SDWebImage 用 URL 做為下載操作的唯一標識。

/// 操作緩存池
@property (nonatomic,strong) NSCache *operationCache;
/// 禮物緩存池
@property (nonatomic,strong) NSCache *userGigtInfos;


把收到的每一條 IM 禮物消息看作是一個動畫操作,這樣就形成了一個禮物隊列,后面的邏輯是,在收到 IM 禮物消息時:

禮物緩存池里有用戶禮物信息

  • 如果操作緩存池沒有動畫操作,創(chuàng)建操作,從上次禮物數量開始累加
  • 如果操作緩存池有動畫操作,直接從上次禮物數量開始累加

禮物緩存池里沒有用戶禮物信息

  • 如果操作緩存池沒有動畫操作,創(chuàng)建操作,從 1 開始累加
  • 如果操作緩存池有動畫操作,直接從上次禮物數量開始累加
/// 動畫操作 : 需要UserID和回調
- (void)animWithUserID:(NSString *)userID model:(GiftModel *)model finishedBlock:(void(^)(BOOL result))finishedBlock {
  
   // 在有用戶禮物信息時
   if ([self.userGigtInfos objectForKey:userID]) {
       // 如果有操作緩存,則直接累加,不需要重新創(chuàng)建op
       if ([self.operationCache objectForKey:userID]!=nil) {
           AnimOperation *op = [self.operationCache objectForKey:userID];
           op.presentView.giftCount = model.giftCount;
           [op.presentView shakeNumberLabel];
           return;
       }
        // 沒有操作緩存,創(chuàng)建op
       AnimOperation *op = [AnimOperation animOperationWithUserID:userID model:model finishedBlock:^(BOOL result,NSInteger finishCount) {
           // 回調
           if (finishedBlock) {
               finishedBlock(result);
           }
           // 將禮物信息數量存起來
           [self.userGigtInfos setObject:@(finishCount) forKey:userID];
           // 動畫完成之后,要移除動畫對應的操作
           [self.operationCache removeObjectForKey:userID];
           // 延時刪除用戶禮物信息
           dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
               [self.userGigtInfos removeObjectForKey:userID];
           });
           
       }];
       
       // 注意:下面兩句代碼是和無用戶禮物信息時不同的,其余的邏輯一樣
       op.presentView.animCount = [[self.userGigtInfos objectForKey:userID] integerValue];
       op.model.giftCount = op.presentView.animCount + 1;
       
       op.listView = self.parentView;
       op.index = [userID integerValue] % 2;
       
       // 將操作添加到緩存池
       [self.operationCache setObject:op forKey:userID];
       
       // 根據用戶ID 控制顯示的位置
       if ([userID integerValue] % 2) {
           
           if (op.model.giftCount != 0) {
               op.presentView.frame  = CGRectMake(-self.parentView.frame.size.width / 2, 300, self.parentView.frame.size.width / 2, 40);
               op.presentView.originFrame = op.presentView.frame;
               [self.queue1 addOperation:op];
           }
       }else {
           
           if (op.model.giftCount != 0) {
               op.presentView.frame  = CGRectMake(-self.parentView.frame.size.width / 2, 240, self.parentView.frame.size.width / 2, 40);
               op.presentView.originFrame = op.presentView.frame;
               [self.queue2 addOperation:op];
           }
       }
   }
   
   // 在沒有用戶禮物信息時
   else
   {   // 如果有操作緩存,則直接累加,不需要重新創(chuàng)建op
       if ([self.operationCache objectForKey:userID]!=nil) {
           AnimOperation *op = [self.operationCache objectForKey:userID];
           op.presentView.giftCount = model.giftCount;
           [op.presentView shakeNumberLabel];
           return;
       }
       
       AnimOperation *op = [AnimOperation animOperationWithUserID:userID model:model finishedBlock:^(BOOL result,NSInteger finishCount) {
           // 回調
           if (finishedBlock) {
               finishedBlock(result);
           }
           // 將禮物信息數量存起來
           [self.userGigtInfos setObject:@(finishCount) forKey:userID];
           // 動畫完成之后,要移除動畫對應的操作
           [self.operationCache removeObjectForKey:userID];
           // 延時刪除用戶禮物信息
           dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
               [self.userGigtInfos removeObjectForKey:userID];
           });
           
       }];
       op.listView = self.parentView;
       op.index = [userID integerValue] % 2;
       // 將操作添加到緩存池
       [self.operationCache setObject:op forKey:userID];
       
       if ([userID integerValue] % 2) {
           
           if (op.model.giftCount != 0) {
               op.presentView.frame  = CGRectMake(-self.parentView.frame.size.width / 2, 300, self.parentView.frame.size.width / 2, 40);
               op.presentView.originFrame = op.presentView.frame;
               [self.queue1 addOperation:op];
           }
       }else {
           
           if (op.model.giftCount != 0) {
               op.presentView.frame  = CGRectMake(-self.parentView.frame.size.width / 2, 240, self.parentView.frame.size.width / 2, 40);
               op.presentView.originFrame = op.presentView.frame;
               [self.queue2 addOperation:op];
           }
       }
   
   }
   
}


禮物信息維持 3s,3s 內不連擊送禮物,禮物信息刪除,從1開始計數;這里大家可以根據項目調整

// 3s不連擊刪除用戶禮物信息  這里可以根據需求自己設定
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        [self.userGigtInfos removeObjectForKey:userID];
});

問題二:由于動畫設計時并沒有明確分段,不能實時收到 IM 消息后累加
解決辦法 :

這里特別感謝 @小蝸牛同學 的幫助,不然動畫流程還需要重新設計下。這里把動畫拆分成了三段,視圖顯示-累加動畫-視圖消失;在累加動畫中,增加了以下代碼:

 [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(hidePresendView) object:nil];//可以取消成功。
 [self performSelector:@selector(hidePresendView) withObject:nil afterDelay:2];

這樣在連續(xù)收到 IM 消息時,可以把之前視圖消失的動畫執(zhí)行取消掉,只執(zhí)行最后收到 IM 禮物消息后,執(zhí)行視圖消失的動畫。

以上便是此次完善的思路,同學們可以根據自己項目進行改造,如果發(fā)現什么 bug 或者好的思路,歡迎大家一起討論交流~祝好。

PS:此次更新比較大,so,發(fā)文說明下。今后如果同學們對這個動畫感興趣,可以關注 github 更新。

Demo 地址:
https://github.com/cooxu/PresentAnimView.git

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

相關閱讀更多精彩內容

  • 最近做了個直播項目,需要用到彈幕和刷禮物。在網上找了許多開源代碼,發(fā)現都不是很適合自己的項目需求,于是利用空余時間...
    Rasping閱讀 4,926評論 23 40
  • 封裝一個動畫任務到操作對象之中? 所有寫在操作對象NSOperation的main方法里面的代碼,都是分線程執(zhí)行。...
    Carden閱讀 825評論 0 2
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,361評論 25 708
  • 這兩天看完了《平凡的世界》電視劇,雖然之前看過原著,但再看一遍電視劇,還是被劇里的故事所感動,忍不住想來這里寫個劇...
    伊柒柒閱讀 4,365評論 1 3
  • 今日上證指數又來了個V型反轉,下午2點半后拉紅,尾盤跳水,最低2908點,最高2937點。 很多人看到上午的盤面,...
    藍家二哥哥閱讀 203評論 0 0

友情鏈接更多精彩內容