前几天自己写了一个类似弹幕的效果
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,能加快多行弹幕的填充速度。
网友评论