写于前面

为便宜大家读,在视频网站上录了相应的视频
怀念看视频的冤家点这里呀~~

<p>何为弹幕?~~</p>

趁着90晚的连突出,弹幕越来越受年轻人的爱。所谓弹幕,我的敞亮就是是评论的其余一样种表现形式,更会抓住用户眼球,增强用户体验,增加用户与感和使用粘度。现在国内比较火之弹幕类视频网站A站和B站,深受年轻人群的追捧。下边就是B站的一个显得效果。

弹幕demo

除此以外一些新闻资讯类的app也开始兑现弹幕功能,为获取用户喜爱,比如唔哩、微在、橘子娱乐等等。下面我们就算来一同分析一下,弹幕在iOS端是什么样兑现之啊?

<p>原理分析与贯彻~~</p>

第一我们来分析一下弹幕的特征。

  • 貌似景象下弹幕都是打屏幕右侧上并自屏幕左侧飞出。
  • 弹幕进入屏幕后以一定轨迹来运动。
  • 弹幕移动速度冲内容长度控制,内容越来越长,移动速度更是快。
  • 一个弹幕完全进入屏幕后,后边会继续飞入一个初的弹幕。
  • 弹幕是循环滚动播放的。

因上述特点,我们设计出来的弹幕形式大体如下图所示,默认只生三只弹道(弹幕飞行轨道)来显示弹幕飞行意义。

  1. 初始化三单弹幕1、2、3预备上屏幕,DataSource为弹幕资源的数目来源地。
1.初始化弹幕
  1. 当弹幕陆陆续续进入屏幕,飞行速度与弹幕长度相关,每当其中一个弹道的弹幕完全进入屏幕后,则由数据池中取出一个弹幕在相应弹道进入屏幕,如弹幕4。
2. 弹幕进入屏幕
  1. 只要有条弹幕已经完全意外起屏幕,则将这弹幕从屏幕被去除,如弹幕1和弹幕3。
3. 弹幕飞出屏幕
  1. 当弹幕全部竟然起屏幕,回到步骤1,重新滚动广播

<p>技术实现~~</p>

脚从技术界来讨论一下实现细节,以下是有些为主代码,完整代码参见这里。
首先来拘禁一下弹幕的生成过程,初始化三独弹幕轨迹,如果不足三个,创建2单或1单轨迹,代码(BulletManager.m)如下:

- (void)start {
    if (self.tmpComments.count == 0) {
        [self.tmpComments addObjectsFromArray:self.allComments];
    }
    self.bStarted = YES;
    self.bStopAnimation = NO;
    [self initBulletCommentView];
}
/**
  *  初始化弹幕
  */
- (void)initBulletCommentView {
    //初始化三条弹幕轨迹
    NSMutableArray *arr = [NSMutableArray arrayWithArray:@[@(0), @(1), @(2)]];
    for (int i = 3; i > 0; i--) {
        NSString *comment = [self.tmpComments firstObject];
        if (comment) {
            [self.tmpComments removeObjectAtIndex:0];
            //随机生成弹道创建弹幕进行展示(弹幕的随机飞入效果)
            NSInteger index = arc4random()%arr.count;
            Trajectory trajectory = [[arr objectAtIndex:index] intValue];
            [arr removeObjectAtIndex:index];
            [self createBulletComment:comment trajectory:trajectory];
        } else {
            break;
        }
    }
}

创办弹幕view,对弹幕view的各种位置状态进行监听并做出相呼应的拍卖。

 /**
  *  创建弹幕
  *
  *  @param comment    弹幕内容
  *  @param trajectory 弹道位置
  */
  - (void)createBulletComment:(NSString *)comment trajectory:(Trajectory)trajectory {
       if (self.bStopAnimation) {
           return;
       }
       //创建一个弹幕view
       BulletView *view = [[BulletView alloc] initWithContent:comment];
       //设置运行轨迹
       view.trajectory = trajectory;
       __weak BulletView *weakBulletView = view;
       __weak BulletManager *myself = self;
       /**
         *  弹幕view的动画过程中的回调状态
         *  Start:创建弹幕在进入屏幕之前
         *  Enter:弹幕完全进入屏幕
         *  End:弹幕飞出屏幕后  
         */             
       view.moveBlock = ^(CommentMoveStatus status) {
           if (myself.bStopAnimation) {
               return ;
           }
           switch (status) {
               case Start:
                   //弹幕开始……将view加入弹幕管理queue
                   [self.bulletQueue addObject:weakBulletView];
                   break;
               case Enter: {
                   //弹幕完全进入屏幕,判断接下来是否还有内容,如果有则在该弹道轨迹对列中创建弹幕……
                   NSString *comment = [myself nextComment];
                   if (comment) {
                       [myself createBulletComment:comment trajectory:trajectory];
                   } else {
                       //说明到了评论的结尾了
                   }
                   break;
               }
               case End: {
                   //弹幕飞出屏幕后从弹幕管理queue中删除
                   if ([myself.bulletQueue containsObject:weakBulletView]) {
                       [myself.bulletQueue removeObject:weakBulletView];
                   }
                   if (myself.bulletQueue.count == 0) {
                       //说明屏幕上已经没有弹幕评论了,循环开始
                       [myself start];
                   }
                   break;
               }
               default:
                  break;
           }
       };    
       //弹幕生成后,传到viewcontroller进行页面展示
       if (self.generateBulletBlock) {
            self.generateBulletBlock(view);
       }
  }
  - (NSString *)nextComment {
     NSString *comment = [self.tmpComments firstObject];
     if (comment) {
         [self.tmpComments removeObjectAtIndex:0];
     }
     return comment;
  }

弹幕view的动画片执行,部分代码(BulletView.m)如下:

  • (void)startAnimation {
    //根据定义之duration计算速度及完全进入屏幕的年月
    CGFloat wholeWidth = CGRectGetWidth(self.frame) + mWidth + 50;
    CGFloat speed = wholeWidth/mDuration;
    CGFloat dur = (CGRectGetWidth(self.frame) + 50)/speed;
    __block CGRect frame = self.frame;
    if (self.moveBlock) {
    //弹幕开始上屏幕
    self.moveBlock(Start);
    }
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(dur *
    NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    //避免重新,通过变量判断是否曾经刑满释放了资源,释放后,不在进展操作。
    //在stopAnimation中 self.bDealloc = YES;
    if (self.bDealloc) {
    return;
    }
    //dur时间后弹幕完全进入屏幕
    if (self.moveBlock) {
    self.moveBlock(Enter);
    }
    });
    //弹幕完全离开屏幕
    [UIView animateWithDuration:mDuration delay:0
    options:UIViewAnimationOptionCurveLinear animations:^{
    frame.origin.x = -CGRectGetWidth(frame);
    self.frame = frame;
    } completion:^(BOOL finished) {
    if (self.moveBlock) {
    self.moveBlock(End);
    }
    [self removeFromSuperview];
    }];
    }

在viewcontroller中一直调用以下代码:

self.bulletManager = [[BulletManager alloc] init];
__weak ViewController *myself = self;
self.bulletManager.generateBulletBlock = ^(BulletView *bulletView) {
    [myself addBulletView:bulletView];
};

- (void)addBulletView:(BulletView *)bulletView {
    bulletView.frame = CGRectMake(CGRectGetWidth(self.view.frame)+50, 200 + 34 * bulletView.trajectory, CGRectGetWidth(bulletView.bounds), CGRectGetHeight(bulletView.bounds));
    [self.view addSubview:bulletView];
    [bulletView startAnimation];
}
//点击开始按钮,弹幕开始飞入屏幕
- (void)clickStart:(UIButton *)btn {
    [self.bulletManager start];
}

末了展示效果如下:

d

查完代码,下载地址。

网站地图xml地图