
這裡實現了以下功能:
隨著收到 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