這裡實現了以下功能:
隨著收到 IM 禮物消息,實時累加禮物數量
如果超出連擊時間,比如 3s ,則重新開始計數,從 1 開始
如果在連擊時間內,則在原先基礎上累加
下面把改造思路跟大家分享下:
這裡引入了一個動畫管理類 AnimOperationManager ,增加了操作緩存池和禮物緩存池,他們都用用戶聊天消息的唯一標識 userID 作為 key,這裡參考了 SDWebImage 用 URL 做為下載操作的唯一標識。
/// 操作緩存池@property (nonatomic,strong) NSCache *operationCache;/// 禮物緩存池@property (nonatomic,strong) NSCache *userGigtInfos;
把收到的每一條 IM 禮物消息看作是一個動畫操作,這樣就形成了一個禮物隊列,後面的邏輯是,在收到 IM 禮物消息時:
禮物緩存池裡有用戶禮物信息
如果操作緩存池沒有動畫操作,創建操作,從上次禮物數量開始累加
如果操作緩存池有動畫操作,直接從上次禮物數量開始累加
禮物緩存池裡沒有用戶禮物信息
如果操作緩存池沒有動畫操作,創建操作,從 1 開始累加
如果操作緩存池有動畫操作,直接從上次禮物數量開始累加
/// 動畫操作 : 需要UserID和回調- (void)animWithUserID:(NSString *)userID model:(GiftModel *)model finishedBlock:(void(^)(BOOL result))finishedBlock { // 在有用戶禮物信息時 if ([self.userGigtInfos objectForKey:userID]) { // 如果有操作緩存,則直接累加,不需要重新創建op if ([self.operationCache objectForKey:userID]!=nil) { AnimOperation *op = [self.operationCache objectForKey:userID]; op.presentView.giftCount = model.giftCount; [op.presentView shakeNumberLabel]; return; } // 沒有操作緩存,創建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 { // 如果有操作緩存,則直接累加,不需要重新創建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]; });
這裡特別感謝 @小蝸牛同學 的幫助,不然動畫流程還需要重新設計下。這裡把動畫拆分成了三段,視圖顯示-累加動畫-視圖消失;在累加動畫中,增加了以下代碼:
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(hidePresendView) object:nil];//可以取消成功。 [self performSelector:@selector(hidePresendView) withObject:nil afterDelay:2];
這樣在連續收到 IM 消息時,可以把之前視圖消失的動畫執行取消掉,只執行最後收到 IM 禮物消息後,執行視圖消失的動畫。
以上便是此次完善的思路,同學們可以根據自己項目進行改造,如果發現什麼 bug 或者好的思路,歡迎大家一起討論交流~祝好。
PS:此次更新比較大,so,發文說明下。今後如果同學們對這個動畫感興趣,可以關注 github 更新。
Demo 地址:
https://github.com/cooxu/PresentAnimView.git