1. 程式人生 > >Objective-C映客房間訊息策略分析與實現

Objective-C映客房間訊息策略分析與實現

最近直播這麼火,今天分析一下映客的房間訊息模組。
映客的房間訊息策略大體上是這樣的:

  • • 訊息積累到一定量之後,便把之前的訊息丟棄掉。
  • • 訊息的顯示是有訊息則自動滾動到底部,如果使用者滑動訊息,則暫時停止滾動,5秒內不作操作則繼續自動滾動。
  • • 訊息在最底部的時候向下滑動並不會暫停自動滾動。
  • • 每一次滑動都會使計時器重置。
  • • 一段時間內如果有大量滾動到最底的訊息,只處理一次。
  • • 房間訊息重新整理速度與訊息頻率相關

主要就分為訊息丟棄和訊息滾動兩大點,下面來一一將講述。

1.訊息丟棄

我們在往訊息tableView的DataSource新增資料的時候檢測陣列的count,有訊息來的話則插入陣列,當count大於一定數量則移除之前的訊息,需要注意的是紅包訊息不應丟棄:

    [_dataArr insertObject:object atIndex:[_dataArr count]];

如果大於一定量的話則從陣列中刪除,視為訊息發生重大變化併發布通知,刷UI。如:

     BOOL afterCleaned = NO;
    if(_dataArr.count > 250){
        [_dataArr removeObjectsInRange:NSMakeRange(0, 200)];
        afterCleaned = YES;
    }
    if(afterCleaned){
        [[NSNotificationCenter
defaultCenter]postNotificationName:@"MessageReconstruct" object:object]; }else{ [[NSNotificationCenter defaultCenter]postNotificationName:@"MessageAppend" object:object]; }

在展示檢視中我們接收到MessageReconstruct和MessageAppend兩個通知,前者為需要重新獲取DataSource並重新整理tableView。後者則直接插入一行即可。

MessageReconstruct:

-(void)notificationReconstructMessage:(NSNotification*)notification{
    // 訊息發生較大變化時,重新載入
    [self reloadDatas];
    // 重新載入,並滾動到最底
    [self scrollToBottomMessage];
}

MessageAppend:

-(void)notificationAppendMessage:(NSNotification*)notification{

    // 新增訊息,不重新載入所有訊息(Cell)
    NSInteger rowCount = [_tableView numberOfRowsInSection:0];
    NSIndexPath *indexPath = [NSIndexPath indexPathForRow:rowCount inSection:0];
    NSArray *indexPaths = [[NSArray alloc]initWithObjects:indexPath, nil];

    // First , prepare the new data
    [_data addObject:notification.object];

    [_tableView insertRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationBottom];

    if(_autoMoveBottom){
        [self scrollToBottomMessage];
    }
}

2.訊息滾動

訊息滾動策略是:
向上滑動時,來新訊息暫時不自動滾屏,5秒內不操作自動滾屏
每一次滑動都會使計時器重置
一段時間內如果有大量滾動到最低的訊息,只處理一次
訊息在最底部的時候向下滑動並不會暫停自動滾動
先來定義兩個滾動標記的bool值來標記是否自動滾動和一個定時器timer:

// 新訊息到來自動滾動到最底下,如果NO,則新增訊息時,不自動滾動
@property (nonatomic, assign) BOOL autoMoveBottom;
@property (nonatomic, strong) NSTimer *timer;
// 有還未執行的ScrollToBottom
@property (nonatomic, assign) BOOL hasPendingScroll;

有訊息滑動到底部:

//有訊息滑動到底部
-(void)scrollToBottomMessage{
    //向上滑動時,來新訊息暫時不自動滾屏,5秒內不操作自動滾屏
    // 一段時間內如果有大量滾動到最低的訊息,只處理一次
    if(_hasPendingScroll)
        return;

    _hasPendingScroll = YES;
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSInteger bottomRow = [_tableView numberOfRowsInSection:0];
        if(bottomRow == 0){
            _hasPendingScroll = NO;
            return;
        }

        [_tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:bottomRow - 1 inSection:0] atScrollPosition:UITableViewScrollPositionBottom animated:YES];
        _hasPendingScroll = NO;
    });
}

在UIScrollViewDelegate控制滾動:

#pragma mark UIScrollViewDelegate
-(void)scrollViewWillBeginDragging:(UIScrollView *)scrollView{
//    _oldContentOffsetY = scrollView.contentOffset.y;
    _autoMoveBottom = NO;
    //5秒不操作有新訊息自動滾屏
    //先暫停計時
    [_timer setFireDate:[NSDate distantFuture]];
    //重置計時器
    _timer = [NSTimer scheduledTimerWithTimeInterval:autoMoveBottom_time target:self selector:@selector(setAutoMoveBottomYes) userInfo:nil repeats:NO];
}
- (void)setAutoMoveBottomYes{
    _autoMoveBottom = YES;
    [_timer invalidate];
    _timer = nil;
}

訊息在最底部的時候向下滑動並不會暫停自動滾動:

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate{

    if(scrollView.contentOffset.y > ((scrollView.contentSize.height - scrollView.frame.size.height))){
        _autoMoveBottom = YES;
    }
}

3.訊息重新整理

當有訊息來的時候可以這樣做:

 _messageRate++;
    if(!_willPerform){
        _willPerform = YES;
        dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC));
        dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
            [self _updataMessageRate];
        });
    }
- (void)_updataMessageRate{
    NSLog(@"訊息頻率:%@/s", @(_messageRate));
    _messageRate = 0;
    _willPerform = NO;
}

儲存下來_messageRate,可以看出訊息頻率每一秒鐘更新一次,這樣就可以根據訊息的頻率來動態制定訊息顯示、重新整理和丟棄策略。

好了,大體就是這樣,不管是IM還是直播房間訊息,看似簡單,其實還是有很多坑的,歡迎大家討論交流。