Objective-C映客房間訊息策略分析與實現
阿新 • • 發佈:2019-01-03
最近直播這麼火,今天分析一下映客的房間訊息模組。
映客的房間訊息策略大體上是這樣的:
- • 訊息積累到一定量之後,便把之前的訊息丟棄掉。
- • 訊息的顯示是有訊息則自動滾動到底部,如果使用者滑動訊息,則暫時停止滾動,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還是直播房間訊息,看似簡單,其實還是有很多坑的,歡迎大家討論交流。