此弹幕来源于直播,所以名为 LiveBarrage 。
这里只分析弹幕实现具体逻辑,详细代码请下载项目根据以下分析理解。
插入弹幕到数组 通过函数- (void)insertBarrages:(NSArray <ZBLiveBarrageCell *> *)barrages 向实例 ZBLiveBarrage 的 dadaArray 属性添加 ZBLiveBarrageCell 弹幕数组。
- (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];}
- (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
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;}
- (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 -1;
- (BOOL)zb_canBarrageSendInTheChannel:(ZBLiveBarrageCell *)oldBarrage newBullet:(ZBLiveBarrageCell *)newBarrage
我们暂且称当前轨道最后一条弹幕为【老弹幕】,将要展示的弹幕为【新弹幕】
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;}
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
- (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)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;
/**
* 弹幕点击事件回调
*/
- (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
原文链接:http://www.cnblogs.com/itzhangbao/p/13937653.html
本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728