前言
此彈幕來源于直播,所以名為 LiveBarrage ,
彈幕效果:

彈幕君說:
- 我會飛~~~(gun~,你咋不上天!!);
- 我的大小你做主;
- 我飛的速度你來定,讓我飛多快我飛多快(PS:恩!真聽話);
- 我的衣服你來買(彈幕樣式自定義);
- 我們彈幕家族自帶磁場從來不會疊加碰撞的哦(⊙o⊙);
- 你點我我就告訴你你點的是我而不是我爸(superView)也不會是我的兄弟姐妹 O(∩_∩)O~:

- 直播彈幕 ** ZBLiveBarrage ** 最懂你!!
目錄結構:

技術剖析:
這里只分析彈幕實作具體邏輯,詳細代碼請下載專案根據以下分析理解,
-
插入彈幕到陣列
通過函式- (void)insertBarrages:(NSArray <ZBLiveBarrageCell *> *)barrages
向實體ZBLiveBarrage的dadaArray屬性添加ZBLiveBarrageCell彈幕陣列, -
創建彈幕
- (void)creatBarrage
{
if (self.dataArray.firstObject) {
// 取出彈幕陣列里第一條未展示的彈幕
ZBLiveBarrageCell *barrageView = self.dataArray.firstObject;
// 通過 函式 zb_canBarrageSendInTheChannel 判斷這條彈幕是否有可用跑道讓其展示
NSInteger row = [self zb_canBarrageSendInTheChannel:barrageView];
// 若果有可用跑到
if (row >= 0) {
barrageView 開始執行 animateWithDuration 在 當前跑道 row 展示彈幕
}
}
// 再次執行 creatBarrage 方法
[self performSelector:@selector(creatBarrage) withObject:nil afterDelay:0.1f];
}
- 判斷最新彈幕是否有可用跑道讓其展示
channelArray與channelCount是對應的,channelCount是外界用來設定彈幕軌道數的屬性,channelArray是用來存放每條軌道上最后一條彈幕,如果沒有彈幕經過軌道默認賦值NSNumber實體,
- (NSInteger)zb_canBarrageSendInTheChannel:(ZBLiveBarrageCell *)newBarrage
{
// 遍歷軌道陣列
for (id object in _channelArray) {
if ([object isKindOfClass:[NSNumber class]]) {
// 如果最后一條沒有最后一條彈幕,回傳當前跑到
return row;
}else if ([object isKindOfClass:[ZBLiveBarrageCell class]]) {
// 獲取最后一條彈幕
ZBLiveBarrageCell *oldBarrage = (ZBLiveBarrageCell*)object;
// 通過 zb_canBarrageSendInTheChannel: newBullet: 函式 實作新彈幕與當前跑道上最后一條彈幕的 碰撞檢測
if ([self zb_canBarrageSendInTheChannel:oldBarrage newBullet:newBarrage]) {
return row;
}
}
}
return -1;
}
- 碰撞檢測
- (BOOL)zb_canBarrageSendInTheChannel:(ZBLiveBarrageCell *)oldBarrage newBullet:(ZBLiveBarrageCell *)newBarrage
回傳值為 BOOL 是否有可能碰撞,思路:
我們暫且稱當前軌道最后一條彈幕為【老彈幕】,將要展示的彈幕為【新彈幕】
if (【老彈幕】還沒完全顯示在螢屏中) {
return NO;
}else if (【老彈幕】的寬度為 0 時) {
// 剛剛添加的控制元件,有可能取到frame的值全為0,也要回傳NO
return NO;
} else if (如果【老彈幕】與【新彈幕】的展示時間相同 && 【老彈幕】的寬度 > 【新彈幕】的寬度) {
// 比較彈幕的寬度(也就是比較速度),如果彈幕在螢屏中停留的時間都一樣,【新彈幕】寬度小于【老彈幕】就是永遠追不上上一條彈幕,回傳YES
return YES;
} else {
// time為新彈幕從出現到螢屏最左邊的時間(此時彈幕整體都在螢屏內,并不是彈幕消失的時間)
CGFloat time = 螢屏寬度/(螢屏寬度+【新彈幕】的寬度)*【新彈幕】的展示時間;
// endX為此時老彈幕的frame的x值
CGFloat endX = 【老彈幕】的 x - time/(【老彈幕展示時間】)*(螢屏寬度 + 【新彈幕】的寬度);
if (endX < -【新彈幕】的寬度) {
// 若此時老彈幕已經完全從螢屏中消失,回傳YES
return YES;
}
}
return NO;
}
- 彈幕點擊事件
因為視圖在影片程序中不能回應手勢,所以只能通過計算來回應用戶的手勢點擊事件
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint clickPoint = [touch locationInView:self];
for (ZBLiveBarrageCell *barrageView in [self subviews])
{
if ([barrageView.layer.presentationLayer hitTest:clickPoint])
{
// 來到這里說明此條彈幕被點擊
}
break;
}
}
}
- 代理事件
/**
* 彈幕點擊事件回呼
*/
- (void)zb_barrageView:(ZBLiveBarrage *)barrageView didSelectedCell:(ZBLiveBarrageCell *)cell;
/**
* 當前插入的彈幕模型陣列全部展示完成回呼
*/
- (void)zb_barrageViewCompletedCurrentAnimations;
/**
* 彈幕即將顯示時回呼
*/
- (void)zb_barrageView:(ZBLiveBarrage *)barrageView willDisplayCell:(ZBLiveBarrageCell *)cell;
/**
* 彈幕顯示完成回呼
*/
- (void)zb_barrageView:(ZBLiveBarrage *)barrageView didEndDisplayingCell:(ZBLiveBarrageCell *)cell;
小結
這一套彈幕實作核心代碼在于彈幕碰撞監測那部分,是不是很簡單那?
注釋很詳細的 Demo 點擊這里
軟體在能夠復用前必須先能用,
——Ralph Johnson
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/229035.html
標籤:iOS
