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

Demo模擬了n個人同時送禮物
這里實現了以下功能:
隨著收到 IM 禮物消息,實時累加禮物數量
如果超出連擊時間,比如 3s ,則重新開始計數,從 1 開始
如果在連擊時間內,則在原先基礎上累加
下面把改造思路跟大家分享下:
問題一:之前動畫已經把禮物數目寫死,如何轉化為 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 更新。