iOS程序员iOS && Android

iOS直播系列之《很会飞的弹幕》

2017-03-16  本文已影响578人  zhmbo

前言

此弹幕来源于直播,所以名为 LiveBarrage 。

弹幕效果:
弹幕效果图.gif
弹幕君说:
弹幕家谱.png

目录结构:

目录结构.png

技术剖析:

这里只分析弹幕实现具体逻辑,详细代码请下载项目根据以下分析理解。

- (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];
}
- (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;
}
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*

上一篇 下一篇

猜你喜欢

热点阅读