【如何快速的開(kāi)發(fā)一個(gè)完整的iOS直播app】(禮物篇)

搭建禮物列表

  • 使用modal,設(shè)置modal樣式為custom,就能做到從小往上顯示禮物列表,并且能看見(jiàn)前面的直播界面
  • 禮物模型設(shè)計(jì)
  • 一開(kāi)始創(chuàng)建3個(gè)禮物模型,保存到數(shù)組,傳入給禮物View展示,本來(lái)禮物數(shù)據(jù)應(yīng)該從服務(wù)器獲取,這里沒(méi)做了。
  • 到時(shí)候拿到禮物View就能拿到對(duì)應(yīng)按鈕,傳給服務(wù)器就好了.

禮物模型設(shè)計(jì)

禮物模型

  • 用戶模型(userID,userName),用于標(biāo)志哪個(gè)用戶發(fā)送,這里為方便測(cè)試,保證UserID一樣
  • 禮物ID(giftID),用于標(biāo)志當(dāng)前禮物
  • 禮物名稱(chēng)(giftName)
  • 禮物總數(shù)(giftCount),用于記錄禮物連發(fā)數(shù),總共發(fā)了多少禮物
  • 發(fā)送禮物的房間Key(roomKey),用于知道是發(fā)送個(gè)哪個(gè)房間
@interface XMGGiftItem : NSObject

// 禮物ID
@property (nonatomic, assign) NSInteger giftId;

// 用戶模型:記錄哪個(gè)用戶發(fā)送
@property (nonatomic, strong) XMGUserItem *user;

// 禮物名稱(chēng)
@property (nonatomic, strong) XMGUserItem *giftName;

// 禮物個(gè)數(shù),用來(lái)記錄禮物的連擊數(shù)
@property (nonatomic, assign) NSInteger giftCount;

// 發(fā)送哪個(gè)房間
@property (nonatomic, strong) NSString *roomKey;

+ (instancetype)giftWithGiftId:(NSInteger)giftId userId:(NSInteger)userId giftCount:(NSInteger)giftCount roomKey:(NSString *)roomKey;

@end


+ (instancetype)giftWithGiftId:(NSInteger)giftId giftCount:(NSInteger)giftCount roomKey:(NSString *)roomKey giftName:(NSString *)giftName
{
    XMGGiftItem *item = [[self alloc] init];
    item.giftId = giftId;
    item.user = [[XMGUserItem alloc] init];
    // ID一樣,模擬只有一個(gè)用戶
    item.user.id = 1;
    item.user.userName = @"用戶1";
    item.giftCount = giftCount;
    item.roomKey = roomKey;
    item.giftName = giftName;
    return item;

}


監(jiān)聽(tīng)禮物點(diǎn)擊

點(diǎn)擊禮物的時(shí)候,發(fā)送禮物

  • 這里使用了websocket搭建的后臺(tái)服務(wù)器,進(jìn)行禮物發(fā)送
    // 發(fā)送禮物
    SocketIOClient *socket = [SocketIOClient shareSocketIOClient];
    
    XMGGiftItem *gift = [XMGGiftItem giftWithGiftId:sender.tag userId:socket.user.id giftCount:1 roomKey:socket.roomKey];
    
    [socket emit:@"gift" with:@[gift.mj_keyValues]];

三、禮物界面監(jiān)聽(tīng)禮物發(fā)送

   // 監(jiān)聽(tīng)禮物
    SocketIOClient *socket = [SocketIOClient shareSocketIOClient];
    
    [socket on:@"gift" callback:^(NSArray * _Nonnull data, SocketAckEmitter * _Nonnull ask) {
        NSLog(@"接收到禮物%@",data);
        XMGGiftItem *item = [XMGGiftItem mj_objectWithKeyValues:data[0]];
        
        // 顯示禮物動(dòng)畫(huà)
        [self setupGiftAnim:item];
    }];

四、設(shè)置禮物動(dòng)畫(huà)

  • 顯示禮物業(yè)務(wù)邏輯

    • 1.并不是每次接收到禮物,都需要?jiǎng)?chuàng)建對(duì)應(yīng)禮物動(dòng)畫(huà)View,一次最多顯示2個(gè)禮物View,當(dāng)執(zhí)行完一個(gè)禮物,就判斷是否還有未執(zhí)行的禮物,繼續(xù)執(zhí)行.
    • 2.需要搞個(gè)禮物隊(duì)列(數(shù)組)保存所有需要執(zhí)行的禮物模型,并不是只保存未執(zhí)行的禮物模型.
      • 2.1 什么是需要執(zhí)行的禮物模型?每一個(gè)需要執(zhí)行的禮物模型都對(duì)應(yīng)一個(gè)禮物View
      • 2.2 如果只保存未執(zhí)行的禮物,不記錄之前的執(zhí)行禮物,沒(méi)法判斷下一個(gè)禮物是否是連發(fā)禮物,因?yàn)槟貌坏街暗淖雠袛唷?/li>
      • 2.3 什么是連發(fā)禮物,同一個(gè)用戶,連續(xù)發(fā)送相同的禮物。
      • 2.4 因此每接收一個(gè)新的禮物,需要與之前的禮物對(duì)比,是否是同一個(gè)人發(fā)送的相同禮物。
    @property (nonatomic, strong) NSMutableArray *giftQueue;
    
    - (NSMutableArray *)giftQueue
    

{
if (_giftQueue == nil) {
_giftQueue = [NSMutableArray array];
}
return _giftQueue;
}

```

*   3.判斷是否是連發(fā)禮物
    * 3.1 遍歷禮物隊(duì)列中所有禮物,判斷當(dāng)前接收的禮物與之前禮物是否有相同的UserID和相同的禮物ID。
    * 3.2 如果有相同的UserID和相同的禮物ID,就表示是連發(fā)禮物,,把禮物模型的禮物總數(shù)+1.
    * 3.3 不需要把連發(fā)禮物添加到禮物隊(duì)列中,因?yàn)橹灰沁B發(fā)禮物就表示之前已經(jīng)有相同的禮物,會(huì)和之前禮物共用同一個(gè)禮物View,不需要?jiǎng)?chuàng)建新的禮物View.
    * 3.4 因此只要是連發(fā)禮物,就直接return,不做操作.

```

pragma mark - 判斷當(dāng)前接收禮物是否屬于連發(fā)禮物

  • (BOOL)isComboGift:(XMGGiftItem *)gift
    {
    XMGGiftItem *comboGift = nil;

    for (XMGGiftItem *giftItem in self.giftQueue) {

      // 如果是連發(fā)禮物就記錄下來(lái)
      if (giftItem.giftId == gift.giftId && giftItem.userId == gift.userId) {
          comboGift = giftItem;
      }
    

    }

    if (comboGift) { // 連發(fā)禮物有值
    // 禮物模型的禮物總數(shù)+1
    comboGift.giftCount += 1;
    return YES;
    }

    return NO;
    }

    
    *   4.如果不是連發(fā)禮物,直接把接收到的禮物添加到禮物隊(duì)列
    *   5.搞個(gè)數(shù)組記錄當(dāng)前顯示的動(dòng)畫(huà)View
        *   5.1 最多顯示兩個(gè)禮物動(dòng)畫(huà)View,記錄當(dāng)前正在做動(dòng)畫(huà)的View
        *   5.2 如果超過(guò)2個(gè)顯示的View,就先不創(chuàng)建禮物View,直接retun
    
    

    @property (nonatomic, strong) NSMutableArray *giftAnimViews;

    • (NSMutableArray *)giftAnimViews
      {
      if (_giftAnimViews == nil) {
      _giftAnimViews = [NSMutableArray array];
      }
      return _giftAnimViews;
      }
    *   6.過(guò)濾掉以上2個(gè)條件之后,`處理禮物動(dòng)畫(huà)`
        *   6.1 創(chuàng)建禮物View
        *   6.2 設(shè)置禮物View的frame
            *   6.2.1 分為上下兩部分,先顯示到底部,在顯示頂部
            *   6.2.2 怎么才知道當(dāng)前禮物View顯示在哪部分,搞個(gè)位置數(shù)組,每次從數(shù)組中取出一個(gè)位置,取完,就移除,這樣下次就不會(huì)顯示重復(fù)的地方了。
        *   6.3 添加禮物View到控制器的View中
        *   6.4 做禮物平移動(dòng)畫(huà)
        *   6.5 禮物平移動(dòng)畫(huà)做完,開(kāi)始做連擊動(dòng)畫(huà)
        
    

    // 處理禮物動(dòng)畫(huà)

  • (void)handleGiftAnim:(XMGGiftItem *)gift
    {
    // 1.創(chuàng)建禮物動(dòng)畫(huà)的View
    XMGGiftAnimView *giftView = [XMGGiftAnimView giftAnimView];

    CGFloat h = self.view.bounds.size.height * 0.5;
    CGFloat w = self.view.bounds.size.width;

    // 取出禮物位置
    id position = self.positions.lastObject;

    // 從數(shù)組移除位置
    [self.positions removeObject:position];

    CGFloat y = [position floatValue] * h;
    // 2.設(shè)置禮物View的frame
    giftView.frame = CGRectMake(0, y, w, h);

    // 3.傳遞禮物模型
    giftView.gift = gift;

    // 記錄當(dāng)前位置
    giftView.tag = [position floatValue];

    // 添加禮物View
    [self.view addSubview:giftView];

    // 添加記錄禮物View數(shù)組
    [self.giftAnimViews addObject:giftView];

    __weak typeof(self) weakSelf = self;

    giftView.dismissView = ^(XMGGiftAnimView *giftView){
    [weakSelf dismissView:giftView];
    };

    // 設(shè)置動(dòng)畫(huà)
    giftView.transform = CGAffineTransformMakeTranslation(-w, 0);
    [UIView animateWithDuration:.25 delay:0 usingSpringWithDamping:0.6 initialSpringVelocity:1 options:UIViewAnimationOptionCurveLinear animations:^{
    giftView.transform = CGAffineTransformIdentity;
    } completion:^(BOOL finished) {
    // 開(kāi)始連擊動(dòng)畫(huà)
    [giftView startComboAnim];
    }];
    }

    
    *   7.禮物連擊動(dòng)畫(huà)
        *   7.1 封裝到禮物View中,禮物需要拿到禮物連擊Label做事情
        *   7.2 每隔一段時(shí)間,需要修改連擊數(shù),搞個(gè)定時(shí)器,每隔0.3秒做事情
        *   7.3 連擊動(dòng)畫(huà),也需要控制在0.3秒剛好做完,就能直接做下一次動(dòng)畫(huà)。
        *   7.4 搞個(gè)屬性記錄當(dāng)前連擊數(shù),沒(méi)執(zhí)行一次連擊就++,當(dāng)當(dāng)前連擊數(shù)大于禮物總數(shù)的時(shí)候,表示連擊動(dòng)畫(huà)執(zhí)行完畢,需要銷(xiāo)毀定時(shí)器,銷(xiāo)毀當(dāng)前禮物View
        *   7.5 `注意點(diǎn)`:當(dāng)當(dāng)前連擊數(shù)大于禮物總數(shù)的時(shí)候,不能馬上確定連擊動(dòng)畫(huà)執(zhí)行完畢,因?yàn)殡娔X執(zhí)行速度大于用戶點(diǎn)擊速度,有可能用戶在點(diǎn)的時(shí)候,沒(méi)有電腦執(zhí)行快,電腦執(zhí)行完直接把禮物View移除了,就看不到連擊效果了。
        *   7.6 因此需要延遲銷(xiāo)毀定時(shí)器,而且只要有新的連擊數(shù)了,需要取消銷(xiāo)毀定時(shí)器,要不然可能連擊數(shù)還沒(méi)顯示完,定時(shí)器就銷(xiāo)毀了
        
    
    • (void)startComboAnim{
      if (_timer == nil) {
      _timer = [NSTimer scheduledTimerWithTimeInterval:0.3 target:self selector:@selector(combo) userInfo:nil repeats:YES];
      _curComboCount = 1;
      }
      }

    // 連擊

  • (void)combo
    {
    // 當(dāng)前連發(fā)數(shù),已經(jīng)顯示完畢
    if (_curComboCount > _gift.giftCount) { // 停止定時(shí)器
    // 停止定時(shí)器
    [self performSelector:@selector(cancel) withObject:nil afterDelay:1];
    } else {

      // 修改label顯示
      _comboView.text = [NSString stringWithFormat:@"x%ld",_curComboCount];
      
      [UIView animateWithDuration:0.15 animations:^{
          _comboView.transform = CGAffineTransformMakeScale(3, 3);
      } completion:^(BOOL finished) {
         [UIView animateWithDuration:0.15 animations:^{
             _comboView.transform = CGAffineTransformIdentity;
         }];
      }];
      
      // 取消定時(shí)器銷(xiāo)毀
      [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(cancel) object:nil];
      
      _curComboCount++;
    

    }
    }

    
    *   8.連擊動(dòng)畫(huà)做完,
        *   8.1 需要停止定時(shí)器
        *   8.2 需要移除禮物動(dòng)畫(huà)的View
        *   8.3 把禮物動(dòng)畫(huà)的View和禮物都移除數(shù)組,需要回到之前控制器,用Block
        *   `注意點(diǎn)`:cancel方法可能會(huì)調(diào)用多次,定時(shí)器沒(méi)有銷(xiāo)毀,就會(huì)一直調(diào)用cancel方法,但是只需要執(zhí)行一次,需要搞個(gè)屬性記錄下.
        *   原因:因?yàn)橐?秒之后才會(huì)調(diào)用cancle,那在這一秒內(nèi),肯定又會(huì)調(diào)用定時(shí)器方法,而且這時(shí)候當(dāng)前連擊數(shù)已經(jīng)大于禮物總數(shù),就會(huì)在1秒內(nèi)多少執(zhí)行cancle方法,導(dǎo)致cancle在1秒內(nèi)調(diào)用多次.
        
        
        ``` 
        - (void)cancel
        {
            if (_isCancel == NO) {
                
                _isCancel = YES;
                
                [_timer invalidate];
                _timer = nil;
                
                if (_dismissView) {
                    _dismissView(self);
                }
            }
        }
        ```
        
    *   9.連擊動(dòng)畫(huà)結(jié)束后執(zhí)行的DismissBlock.
        *   9.1 做禮物View移除動(dòng)畫(huà),往上移動(dòng),透明度為0
        *   9.2 把禮物模型從隊(duì)列移除
        *   9.3 把禮物View從顯示的禮物View數(shù)組移除
        *   9.4 移除當(dāng)前View
        *   9.5 恢復(fù)位置到位置數(shù)組
            *   9.3.1 怎么知道恢復(fù)哪個(gè)位置?可以用禮物View的tag記錄當(dāng)前禮物View的位置
            *   9.3.2 如果當(dāng)前tag為0,需要插入第0個(gè)位置,其他情況使用addObject.
        *   9.6 當(dāng)一個(gè)禮物做完動(dòng)畫(huà),查看隊(duì)列中是否還有未執(zhí)行的禮物。
        
        ```
                - (void)dismissView:(XMGGiftAnimView *)giftView
        {
            
            [UIView animateWithDuration:0.25 animations:^{
                
                giftView.alpha = 0;
                giftView.transform = CGAffineTransformMakeTranslation(0, -20);
                
            } completion:^(BOOL finished) {
                
                // 移除當(dāng)前禮物
                [self.giftQueue removeObject:giftView.gift];
                
                // 移除當(dāng)前動(dòng)畫(huà)的View
                [giftView removeFromSuperview];
                
                // 移除禮物動(dòng)畫(huà)View數(shù)組
                [self.giftAnimViews removeObject:giftView];
                
                // 恢復(fù)當(dāng)前位置
                if (giftView.tag == 0) {
                    // 插入第0個(gè)位置
                    [self.positions insertObject:@(0) atIndex:0];
                } else {
                    [self.positions addObject:@(giftView.tag)];
                }
                
                // 判斷隊(duì)列中是否還有未處理的禮物
                XMGGiftItem *item = [self fetchNoHandleGiftItemOfQueue];
                
                // 處理禮物動(dòng)畫(huà)
                if (item) {
                    [self handleGiftAnim:item];
                }
                
            }];
            
        }
        ```
    *   10.執(zhí)行完一個(gè)禮物,判斷禮物隊(duì)列是否還有未執(zhí)行的禮物
        *   10.1 遍歷禮物隊(duì)列中所有禮物,查看是否有未執(zhí)行的禮物
        *   10.2 取出的禮物,有可能是當(dāng)前正在執(zhí)行的禮物,需要排除掉
            *   10.2.1 遍歷當(dāng)前正在執(zhí)行的禮物View,查看取出的禮物是否和它的禮物相同,相同表示當(dāng)前禮物在執(zhí)行
        *   10.3 獲取到未執(zhí)行的禮物,直接處理禮物
        ```
        // 搜索禮物隊(duì)列中未執(zhí)行的禮物
        - (XMGGiftItem *)fetchNoHandleGiftItemOfQueue
        {
        
            // 取出隊(duì)列中的禮物
            for (XMGGiftItem *item in self.giftQueue) {
                // 當(dāng)前禮物模型有可能在執(zhí)行
                if (![self isExcutingGift:item]) return item;
            }
            
            return nil;
        }
        
        // 判斷當(dāng)前禮物是否正在執(zhí)行
        - (BOOL)isExcutingGift:(XMGGiftItem *)gift
        {
            // 判斷當(dāng)前模型是否已經(jīng)在執(zhí)行,執(zhí)行就不需要在做動(dòng)畫(huà)
            for (XMGGiftAnimView *giftView in self.giftAnimViews) {
                
                if (giftView.gift == gift) return YES;
            }
            
            return NO;
        }
        
        ```
        
    
  • 禮物整體業(yè)務(wù)邏輯

        - (void)setupGiftAnim:(XMGGiftItem *)gift
        {
            // 1.判斷當(dāng)前接收的禮物是否屬于連發(fā)禮物 屬于直接return,不需要在重新創(chuàng)建新的動(dòng)畫(huà)View
            if ([self isComboGift:gift]) return;
            
            // 2.添加到禮物隊(duì)列
            [self.giftQueue addObject:gift];
            
            // 3.判斷當(dāng)前顯示多少個(gè)禮物動(dòng)畫(huà)View
            if (self.giftAnimViews.count == 2) return;
            
            // 4.處理禮物動(dòng)畫(huà)
            [self handleGiftAnim:gift];
        }
    
    
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容