美文网首页
iOS 弹幕UI的思路

iOS 弹幕UI的思路

作者: 万年老参 | 来源:发表于2020-02-21 19:23 被阅读0次

    前几天自己写了一个类似弹幕的效果


    IMG_2452.GIF

    思路:

    一 首先‘’是UI:

    1所有弹幕都要放在其父视图中,父视图设置

    .layer.masksToBounds = YES;
    

    2弹幕移动:
    一般是从父视图一侧移动到另一侧,可以匀速运动,需要根据弹幕字符长度计算运动时间,也可以固定时间。

    //我这里用的是约束,也可以不用约束,
    //匀速运动需要计算时间,view是弹幕,rightBigView是我弹幕的父视图,
        float time = ((view.frame.origin.x +view.frame.size.width)/self.rightBigView.frame.size.width)*5.0;
    //起始位置:如果当前行没有正在展示的弹幕,则添加到父视图最右侧外面,如果有展示的数据,则根据展示数据尾巴是否进入父视图,判断是添加在尾巴位置还是添加到父视图最右侧外面。
    //最终全部移动出父视图最左侧。
        float afterX = view.frame.size.width;
        //动画
    //view为自定义的弹幕视图,
        [UIView animateWithDuration:time delay:0 options:UIViewAnimationOptionCurveLinear animations:^{
            [view mas_updateConstraints:^(MASConstraintMaker *make) {
    //            make.top.mas_equalTo(self.rightBigView.mas_top).mas_offset(.origin.y);
                make.left.mas_equalTo(self.rightBigView.mas_left).mas_offset(-afterX);
            }];
            [view.superview layoutIfNeeded];//强制绘制
        } completion:^(BOOL finished) {
    //我这里是三行弹幕,移动完毕之后,移除。
            if (number == 1) {
                [self.topViewLine removeObject:view];
            }else if (number == 2){
                [self.middleViewLine removeObject:view];
            }else if (number == 3){
                [self.downViewLine removeObject:view];
            }
            [view removeFromSuperview];
        }];
    

    二 逻辑

    数据逻辑:

    创建一个可变数组,构建弹幕所需要的数据:(头像,name,弹幕内容等)存放在可变数组里。
    新的数据添加到数组末尾(通知或者长链接接收到的弹幕数据格式化后),实现可动态添加数据。

    创建了一个NSTime循环每1秒检测一次(用通知或者别的也可以 有优化空间)。(如果不需要在UI操作时暂停弹幕的话,需要在runloop中解决主线程UI操作会暂停计时器的问题)

    self.mytime = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(showDataFromSelfArray) userInfo:nil repeats:YES];
    ///这里是让NSTime不被UI阻碍。
    [[NSRunLoop mainRunLoop] addTimer:self.mytime forMode:NSRunLoopCommonModes];
    

    定时器任务:showDataFromSelfArray,每隔一定时间从数据源中取第一条数据构建弹幕并展示。

    //返回YES则表示将数据源传递到UI层了,返回NO则表示无剩余数据或UI层在排队等待。
    -(BOOL)showDataFromSelfArray
    {
        if (self.dataAry.count<=0) {
    //没有新的弹幕了,跳过。也可以在这里加一些假数据,呈现弹幕繁荣的样子。
            return NO;
        }
    //我是三行弹幕,
    //判断当前三行弹幕是不是都有正在显示的弹幕,并且取每行的最后一个判断是否已经开始展示了,
        if (self.topViewLine.count>0 &&self.middleViewLine.count>0&&self.downViewLine.count>0) {
            UIView *view1 = self.topViewLine.lastObject;
            UIView *view2 = self.middleViewLine.lastObject;
            UIView *view3 = self.downViewLine.lastObject;
            int top = view1.frame.origin.x;
            int middle = view2.frame.origin.x;
            int down = view3.frame.origin.x;
            if (top > self.rightBigView.frame.size.width && middle > self.rightBigView.frame.size.width && down >self.rightBigView.frame.size.width) {
      //此时三个弹幕行都有在屏幕外排队等待的弹幕(前几秒添加的弹幕比较长),本次定时任务跳过。
                return NO;
            }
        }
    //取当前需要显示的弹幕顺序的第一位。作为即将展示的数据,并将其从数据源中移除,
        NSDictionary *dic = self.dataAry[0];
        [self showDataWithDic:dic];
        [self.dataAry removeObject:dic];
        return YES
    }
    

    根据数据源构建弹幕并展示showDataWithDic:。也可以单独调用showDataWithDic,来实现定时任务之外的临时加入的弹幕(插队弹幕)

    //此方法待优化,可以增加BOOL返回值,返回立即展示还是排队等待
    -(void)showDataWithDic:(NSDictionary *)dic
    {
        //判断是否在第一行展示
        if (self.topViewLine.count<=0) {
    //第一行没有弹幕,直接展示在第一行
            [self showData:dic inLine:1];
            return;
        }else{
    //第一行有弹幕,但是最后一条弹幕已经全部展示,尾部有空间了,展示在第一行
            UIView *view = self.topViewLine.lastObject;
            if (view.layer.presentationLayer.frame.origin.x+view.frame.size.width < self.rightBigView.frame.size.width) {
                [self showData:dic inLine:1];
                return;
            }
        }
        //判断是否在第二行展示
        if (self.middleViewLine.count<=0) {
            [self showData:dic inLine:2];
            return;
        }else{
            UIView *view = self.middleViewLine.lastObject;
            if (view.layer.presentationLayer.frame.origin.x +view.frame.size.width < self.rightBigView.frame.size.width) {
                [self showData:dic inLine:2];
                return;
            }
        }
        //判断是否在第三行展示
        if (self.downViewLine.count<=0) {
            [self showData:dic inLine:3];
            return;
        }else{
            UIView *view = self.downViewLine.lastObject;
            if (view.layer.presentationLayer.frame.origin.x +view.frame.size.width < self.rightBigView.frame.size.width) {
                [self showData:dic inLine:3];
                return;
            }
        }
        //三行,判断哪一行最短,添加在这一行末端,
        UIView *view1 = self.topViewLine.lastObject;
        UIView *view2 = self.middleViewLine.lastObject;
        UIView *view3 = self.downViewLine.lastObject;
        int top = view1.layer.presentationLayer.frame.origin.x + view1.layer.presentationLayer.frame.size.width;
        int middle = view2.layer.presentationLayer.frame.origin.x + view2.layer.presentationLayer.frame.size.width;
        int down = view3.layer.presentationLayer.frame.origin.x + view3.layer.presentationLayer.frame.size.width;
        if (top<middle && top <down) {
            [self showData:dic inLine:1];
        }else{
            if (middle < down) {
                [self showData:dic inLine:2];
            }else{
                [self showData:dic inLine:3];
            }
        }
    }
    

    动态添加数据源时,可能会出现数据源添加速度,远超弹幕展示速度,此时可能造成数据堆积,导致程序出现问题,所以添加数据源时需要控制最大数据量,超过最大数据量时可以选择:
    1,移除最早的数据源(即会有些数据源直接丢弃,不会展示)
    2,增加弹幕行或允许弹幕覆盖弹幕。。
    其它优化:
    多行弹幕可以在开始时,可以循环调用showDataFromSelfArray至返回值为NO,能加快多行弹幕的填充速度。

    相关文章

      网友评论

          本文标题:iOS 弹幕UI的思路

          本文链接:https://www.haomeiwen.com/subject/byqrqhtx.html