【全細(xì)節(jié)分析】- iOS端直播間禮物模塊—2019

這個是從已上線半年的直播項目中抽出來的禮物模塊,通過壓測無任何問題钳宪。

準(zhǔn)備1. 生成禮物模型

@interface NDGiftModel : NSObject

@property (nonatomic, strong) NDGifts *gift;
@property (nonatomic, strong) NDUserModel *user;

/** 禮物操作的唯一Key 由用戶ID+禮物ID生成 */
@property (nonatomic, copy) NSString *giftKey;
// 氣泡動畫顯示時間 秒
@property (nonatomic, assign) CGFloat time;
// 單次收到禮物的數(shù)量
@property (nonatomic, assign) NSInteger giftCount;
// 禮物連擊上限
@property (nonatomic, assign) NSInteger doubleHitCount;

@end

準(zhǔn)備2. 了解我們的禮物動畫運行過程

/** 動畫過程
    這是一個普遍的禮物動畫過程晾匠,
    當(dāng)然你可以根據(jù)自身業(yè)務(wù)調(diào)整
 */
typedef NS_ENUM(NSInteger, NDAnimationStatus) {
    NDAnimationStatusUnknown = 0,
    NDAnimationStatusStart,     // 開始運行動畫,從左邊橫向出現(xiàn)的動畫(0.2s)
    NDAnimationStatusSerial,    // 連擊動畫中~美浦,一個放大縮小動畫(0.3s)
    NDAnimationStatusStop,      // 動畫已停止输莺,懸浮在視圖上(默認(rèn)2秒戚哎,可根據(jù)??調(diào)整時間)
    NDAnimationStatusEnd,       // 動畫將結(jié)束,視圖向上的漸隱消失動畫(0.2s)
};

初始化我們的禮物管理器

- (instancetype)initWithView:(UIView *)bearView {
    if (self = [super init]) {
        
        // 沒有做屏幕適配嫂用,可自行調(diào)整
        CGFloat _width = 260;
        CGFloat _maxY = [UIScreen mainScreen].bounds.size.height / 2 - 48;
        for (int i = 0; i<2; i++) {
            NDGiftAnimationView *animationV = [[NDGiftAnimationView alloc] init];
            if (i == 1) {
                animationV.frame = CGRectMake(-_width, _maxY, _width, 40);
            } else {
                animationV.frame = CGRectMake(-_width, 40+8+_maxY, _width, 40);
            }
            [bearView addSubview:animationV];
            [self.animationArray addObject:animationV];
        }
    }
    return self;
}

1. 客戶端收到禮物

// 收到禮物
- (void)receivedGift:(NDGiftModel *)gift {
    if (!gift) return;
    
    // 更新總數(shù)量
    gift.doubleHitCount = gift.giftCount;
    
    // 1. 判讀當(dāng)前禮物視圖是否需要顯示動畫
    for (NDGiftAnimationView *giftView in self.animationArray) {
        BOOL update = [giftView animationStatusWith:gift];
        if (update) {
            return;
        }
    }
    
    // 2. 追加|更新禮物隊列
    [self insertOrUpdate:gift];
    
    // 3. 執(zhí)行禮物隊列動畫
    [self animateNextGift];
}

1.1 根據(jù)禮物視圖狀態(tài)決定接下來的操作

- (BOOL)animationStatusWith:(NDGiftModel *)gift {
    // 是否同用戶同禮物  判斷唯一的key
    if ([self.currentGift.giftKey isEqualToString:gift.giftKey]) {
        // 禮物即將結(jié)束或者處于未啟動狀態(tài)
        if (self.animationStatus == NDAnimationStatusUnknown || self.animationStatus == NDAnimationStatusEnd) {
            return NO;
        }
        // 禮物處于開始動畫中
        if (self.animationStatus == NDAnimationStatusStart) {
            self.currentGift.giftCount = gift.giftCount;
            self.currentGift.doubleHitCount += self.currentGift.giftCount;
            return YES;
        }
        // 禮物處于連擊狀態(tài)
        if (self.animationStatus == NDAnimationStatusSerial) {
            self.currentGift.giftCount = gift.giftCount;
            self.currentGift.doubleHitCount += self.currentGift.giftCount;
            return YES;
        }
        // 禮物停止運行動畫型凳,處于停止中
        if (self.animationStatus == NDAnimationStatusStop) {
            self.currentGift.giftCount = gift.giftCount;
            self.currentGift.doubleHitCount += self.currentGift.giftCount;
            // 連擊
            [self doShakeNumberLabel];
            return YES;
        }
    }
    return NO;
}

從上面的禮物視圖狀態(tài):

  1. 當(dāng)動畫未開始和即將結(jié)束我們直接返回 NO,
  2. 動畫即將開始了嘱函,這個時候我們又接收到同樣的禮物我們只需要更新數(shù)量就可以了
  3. 動畫處于連擊狀態(tài)中甘畅,同樣的只需要更新禮物數(shù)量
  4. 動畫處于懸浮在頁面上,動畫也停止了实夹,這個時候我們需要更新數(shù)量橄浓,并且從新啟動連擊動畫。

1.2 動畫開始中~~

- (void)startAnimationWithGift:(NDGiftModel *)gift finishedBlock:(void (^)(NDGiftModel * _Nonnull))finishedBlock {
    
    self.animationStatus = NDAnimationStatusStart;
    self.currentGift = gift;
    
    self.finishedBlock = finishedBlock;
    self.originFrame = self.frame;
    
    NDWeakSelf
    [UIView animateWithDuration:AnimationStartDuration animations:^{
        weakSelf.alpha = 1.0;
        // 該動畫是將X軸設(shè)置為0 橫向移動效果
        weakSelf.x = 0;
    } completion:^(BOOL finished) {
        [weakSelf doShakeNumberLabel];
    }];
}

1.3 動畫連擊中

- (void)doShakeNumberLabel {
    [self cleanDelayedBlockHandle];
    
    self.animationStatus = NDAnimationStatusSerial;
    _currentIndex = self.currentGift.doubleHitCount;
    self.animationImgView.showCount = _currentIndex;
    
    NDWeakSelf;
    [self.animationImgView startAnimWithDuration:AnimationSerialDuration complate:^{
        // 判斷禮物連擊是否達(dá)到上限 
        if (weakSelf.currentIndex >= weakSelf.currentGift.doubleHitCount) {
            
            // 更新禮物狀態(tài)處于靜止中
            weakSelf.animationStatus = NDAnimationStatusStop;
            weakSelf.delayedBlockHandle = perform_block_after_delay(weakSelf.timeFloat, ^{
                [weakSelf endAnimation];
            });
        } else { // 遞歸 繼續(xù)連擊
            [weakSelf doShakeNumberLabel];
        }
    }];
}

1.4 動畫即將結(jié)束

- (void)endAnimation {
    self.animationStatus = NDAnimationStatusEnd;
    NDWeakSelf;
    // 該動畫是向上移動一小段距離的效果 隱藏
    [UIView animateWithDuration:AnimationEndDuration delay:0 options:UIViewAnimationOptionCurveEaseOut animations:^{
        weakSelf.y -= 10;
        weakSelf.alpha = 0.0; // 漸變逐漸隱藏
    } completion:^(BOOL finished) {
        weakSelf.frame = weakSelf.originFrame;
        weakSelf.alpha = 0.0;
        weakSelf.animationStatus = NDAnimationStatusUnknown;
        weakSelf.currentGift = nil;
        if (weakSelf.finishedBlock) {
            weakSelf.finishedBlock(weakSelf.currentGift);
        }
    }];
}

2. 追加|更新禮物隊列

- (void)insertOrUpdate:(NDGiftModel *)model {
    // 遍歷相同禮物累加
    for (NDGiftModel *item in self.giftArray) {
        if ([item.giftKey isEqualToString:model.giftKey]) {
            item.giftCount = model.giftCount;
            item.doubleHitCount += item.giftCount;
            return;
        }
    }
    // 優(yōu)先級插入(價格高的在前)
    // [obj.gift.contributions floatValue] < [model.gift.contributions floatValue])
    
    [self.giftArray addObject:model];
}

3. 執(zhí)行禮物隊列動畫

/** 執(zhí)行禮物動畫 */
- (void)animateNextGift {
    // 1. 沒有要顯示的禮物
    NDGiftModel *gift = self.giftArray.firstObject;
    if (!gift) {
        return;
    }
    // 2. 執(zhí)行禮物動畫
    NDWeakSelf;
    for (NDGiftAnimationView *animationView in self.animationArray) {
        if (animationView.animationStatus == NDAnimationStatusUnknown) {
            
            [weakSelf.giftArray removeObject:gift];
            
            [animationView startAnimationWithGift:gift finishedBlock:^(NDGiftModel *gift) {
                
                // 執(zhí)行完動畫遞歸
                [weakSelf animateNextGift];
            }];
            return;
        }
    }
}

最后就是demo地址啦~~~~ github地址

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末亮航,一起剝皮案震驚了整個濱河市荸实,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌缴淋,老刑警劉巖准给,帶你破解...
    沈念sama閱讀 218,941評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異重抖,居然都是意外死亡露氮,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評論 3 395
  • 文/潘曉璐 我一進(jìn)店門钟沛,熙熙樓的掌柜王于貴愁眉苦臉地迎上來畔规,“玉大人,你說我怎么就攤上這事恨统∪ǎ” “怎么了?”我有些...
    開封第一講書人閱讀 165,345評論 0 356
  • 文/不壞的土叔 我叫張陵畜埋,是天一觀的道長莫绣。 經(jīng)常有香客問我,道長悠鞍,這世上最難降的妖魔是什么对室? 我笑而不...
    開封第一講書人閱讀 58,851評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上掩宜,老公的妹妹穿的比我還像新娘蔫骂。我一直安慰自己,他們只是感情好锭亏,可當(dāng)我...
    茶點故事閱讀 67,868評論 6 392
  • 文/花漫 我一把揭開白布纠吴。 她就那樣靜靜地躺著硬鞍,像睡著了一般慧瘤。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上固该,一...
    開封第一講書人閱讀 51,688評論 1 305
  • 那天锅减,我揣著相機(jī)與錄音,去河邊找鬼伐坏。 笑死怔匣,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的桦沉。 我是一名探鬼主播每瞒,決...
    沈念sama閱讀 40,414評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼纯露!你這毒婦竟也來了剿骨?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,319評論 0 276
  • 序言:老撾萬榮一對情侶失蹤埠褪,失蹤者是張志新(化名)和其女友劉穎浓利,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體钞速,經(jīng)...
    沈念sama閱讀 45,775評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡贷掖,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了渴语。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片苹威。...
    茶點故事閱讀 40,096評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖驾凶,靈堂內(nèi)的尸體忽然破棺而出牙甫,到底是詐尸還是另有隱情,我是刑警寧澤狭郑,帶...
    沈念sama閱讀 35,789評論 5 346
  • 正文 年R本政府宣布腹暖,位于F島的核電站,受9級特大地震影響翰萨,放射性物質(zhì)發(fā)生泄漏脏答。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,437評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望殖告。 院中可真熱鬧阿蝶,春花似錦、人聲如沸黄绩。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽爽丹。三九已至筑煮,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間粤蝎,已是汗流浹背真仲。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留初澎,地道東北人秸应。 一個月前我還...
    沈念sama閱讀 48,308評論 3 372
  • 正文 我出身青樓,卻偏偏與公主長得像碑宴,于是被迫代替她去往敵國和親软啼。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,037評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 封裝一個動畫任務(wù)到操作對象之中? 所有寫在操作對象NSOperation的main方法里面的代碼延柠,都是分線程執(zhí)行祸挪。...
    Carden閱讀 682評論 0 2
  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴(yán)謹(jǐn) 對...
    cosWriter閱讀 11,103評論 1 32
  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫、插件捕仔、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 12,105評論 4 62
  • --268天 去同行的寵物店感受銷售方法匕积,不同區(qū)域,不同管理理念榜跌,不同消費群體闪唆,銷售模式也不一樣,只要能把客人...
    Alina_qi閱讀 154評論 0 0
  • 光陰謠 (致小冠老師) 唐星河 初秋的天氣暖和 相逢的人終于再相逢了 光線在落 果實在落 萬物都在...
    唐星河閱讀 320評論 0 1