gpt4 book ai didi

ios弹幕高效加载实现方式实例代码

转载 作者:qq735679552 更新时间:2022-09-27 22:32:09 25 4
gpt4 key购买 nike

CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.

这篇CFSDN的博客文章ios弹幕高效加载实现方式实例代码由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.

看直播的童鞋们应该会经常看到满屏幕的滚动弹幕,看到密密麻麻的弹幕第一印象就是怎么样高效加载来避免卡顿,弹幕组成部分包含用户头像、用户昵称、弹幕的内容、表情等,本文介绍的实现原理就是把这几部分绘制成一张图片,然后通过定时器移动弹幕图片,当图片不在屏幕范围内即销毁.

先看下效果 。

ios弹幕高效加载实现方式实例代码

ios弹幕高效加载实现方式实例代码

下面我会详细介绍下实现原理 。

1 .获取弹幕数据来源,因为我是模拟生成弹幕,弹幕的数据存放在工程里的plist文件中 。

ios弹幕高效加载实现方式实例代码

emotions存放这条弹幕的表情,type表示是否是自己发的,text表示弹幕内容,username表示用户昵称。取出plist文件的数据并转换成model.

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#pragma mark - 获取数据源
- ( void )loaddata{
   // 获取plist全路径
   nsstring *filepath = [[nsbundle mainbundle] pathforresource:@ "barrage.plist" oftype:nil];
   // 从指定的路径中加载数据
   nsarray *array = [nsarray arraywithcontentsoffile:filepath];
 
   // 遍历数组
   for (nsdictionary *dict in array) {
     // 字典转模型
     bamodle *barragem = [bamodle barragewithdict:dict];
     [self.danmus addobject:barragem];
   }
}

2 .根据模型生成弹幕图片,通过点击屏幕生成模型,根据模型绘制图片.

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#pragma mark - 触摸屏幕响应事件
- ( void )touchesbegan:(nsset<uitouch *> *)touches withevent:(uievent *)event{
 
   // 获得一个随机整数
   nsinteger index = arc4random_uniform((u_int32_t)self.danmus.count);
   // 获得一个随机模型
   bamodle *danmu = self.danmus[index];
   // 根据模型生成图片
   baimage *image = [self.danmuview imagewithbarrage:danmu];
   // 调整弹幕加载区域
   image.x = self.view.bounds.size.width;
   image.y = arc4random_uniform(self.danmuview.bounds.size.height - image.size.height);
   // 把图片加到弹幕view上
   [self.danmuview addimage:image];
}

下面是具体绘制弹幕图片过程,我先简单介绍下,首先在绘图之前要确定上下文的尺寸,相当于画板的大小,画板的长 = 头像的长 + 昵称的长 + 内容的长 + 表情的长 * 表情个数 + 间距。然后就是分别绘制背景图片,用户昵称,内容和表情,最后返回一张图片.

此处有两点需要注意:

1.由于头像是矩形,想显示成圆形,要先画一个圆,并设置超出圆形的部分要裁剪,再绘制头像.

2.由于上面设置超出圆形的部分要裁剪,那即将要绘制背景岂不是要被裁剪,所以在绘制圆形区域上一句执行了cgcontextsavegstate(ctx)表示复制了一份画板(上下文)存到栈里,在绘制背景图片之前执行cgcontextrestoregstate(ctx),表示用之前保存的画板替换当前的,因为之前保存的画板没有设置超出圆形区域要裁剪的需求,当然替换当前的画板,会把当前画板上的绘图也copy过去.

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
#pragma mark - 绘制弹幕图片
- (baimage *)imagewithbarrage:(bamodle *)danmu{
   // 开启绘图上下文
   //
   uifont *font = [uifont systemfontofsize:13];
   // 头像
   cgfloat iconh = 30;
   cgfloat iconw = iconh;
   // 间距
   cgfloat marginx = 5;
 
   // 表情的尺寸
   cgfloat emotionw = 25;
   cgfloat emotionh = emotionw;
   // 计算用户名占据的区域
   cgsize namesize = [danmu.username boundingrectwithsize:cgsizemake(maxfloat, maxfloat) options:nsstringdrawinguseslinefragmentorigin attributes:@{nsfontattributename:font} context:nil].size;
   // 计算内容占据的区域
   cgsize textsize = [danmu.text boundingrectwithsize:cgsizemake(maxfloat, maxfloat) options:nsstringdrawinguseslinefragmentorigin attributes:@{nsfontattributename:font} context:nil].size;
 
   // 位图上下文的尺寸
   cgfloat contenth = iconh;
   cgfloat contentw = iconw + 4 * marginx + namesize.width + textsize.width + danmu.emotions.count * emotionh;
 
   cgsize contextsize = cgsizemake(contentw, contenth);
   uigraphicsbeginimagecontextwithoptions(contextsize, no, 0.0);
 
   // 获得位图上下文
   cgcontextref ctx = uigraphicsgetcurrentcontext();
   // 将上下文保存到栈中
   cgcontextsavegstate(ctx);
   // 1.绘制圆形区域
   cgrect iconframe = cgrectmake(0, 0, iconw, iconh);
   // 绘制头像圆形
   cgcontextaddellipseinrect(ctx, iconframe);
   // 超出圆形的要裁剪
   cgcontextclip(ctx);
   // 2.绘制头像
   uiimage *icon = danmu.type ? [uiimage imagenamed:@ "headimage_1" ]:[uiimage imagenamed:@ "headimage_2" ];
   [icon drawinrect:iconframe];
   // 将上下文出栈替换当前上下文
   cgcontextrestoregstate(ctx);
   // 3.绘制背景图片
   cgfloat bgx = iconw + marginx;
   cgfloat bgy = 0;
   cgfloat bgw = contentw - bgx;
   cgfloat bgh = contenth;
   danmu.type ? [[uicolor orangecolor] set]:[[uicolor whitecolor] set];
   [[uibezierpath bezierpathwithroundedrect:cgrectmake(bgx, bgy, bgw, bgh) cornerradius:20.0] fill];
 
   // 4.绘制用户名
   cgfloat namex = bgx + marginx;
   cgfloat namey = (contenth - namesize.height) * 0.5;
   [danmu.username drawatpoint:cgpointmake(namex, namey) withattributes:@{nsattachmentattributename:font,nsforegroundcolorattributename:danmu.type == no ? [uicolor orangecolor]:[uicolor blackcolor]}];
 
   // 5.绘制内容
   cgfloat textx = namex + namesize.width + marginx;
   cgfloat texty = namey;
   [danmu.text drawatpoint:cgpointmake(textx, texty) withattributes:@{nsattachmentattributename:font,nsforegroundcolorattributename:danmu.type == no ? [uicolor blackcolor]:[uicolor whitecolor]}];
 
   // 6.绘制表情
   __block cgfloat emotionx = textx + textsize.width;
   cgfloat emotiony = (contenth - emotionh) * 0.5;
   [danmu.emotions enumerateobjectsusingblock:^(nsstring *emotionname, nsuinteger idx, bool * _nonnull stop) {
     // 加载表情图片
     uiimage *emotion = [uiimage imagenamed:emotionname];
     [emotion drawinrect:cgrectmake(emotionx, emotiony, emotionw, emotionh)];
     // 修改emotionx
     emotionx += emotionw;
   }];
   // 从位图上下文中获得绘制好的图片
   uiimage *image = uigraphicsgetimagefromcurrentimagecontext();
 
   return [[baimage alloc] initwithcgimage:image.cgimage scale:[uiscreen mainscreen].scale orientation:uiimageorientationup];
}

3 .开启绘图定时器,回调方法是setneedsdisplay,这样就会执行- (void)drawrect:(cgrect)rect每次修改image.x(由于uiimage没有x、y属性,所以写了个类拓展baimage),滚动不在屏幕范围内的会销毁 。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#pragma mark - 添加定时器
- ( void )addtimer{
   if (self.link) {
     return ;
   }
   // 每秒执行60次回调
   cadisplaylink *link = [cadisplaylink displaylinkwithtarget:self selector:@selector(setneedsdisplay)];
   // 将定时器添加到runloop
   [link addtorunloop:[nsrunloop currentrunloop] formode:nsrunloopcommonmodes];
   self.link = link;
}
#pragma mark - 绘制移动
- ( void )drawrect:(cgrect)rect{
 
   for (baimage *image in self.imagearray) {
     image.x -= 3;
     // 绘制图片
     [image drawatpoint:cgpointmake(image.x, image.y)];
     // 判断图片是否超出屏幕
     if (image.x + image.size.width < 0) {
       [self.deleteimagearray addobject:image];
     }
   }
   // 移除超过屏幕的弹幕
   for (baimage *image in self.deleteimagearray) {
     [self.imagearray removeobject:image];
   }
   [self.deleteimagearray removeallobjects];
}

最后附上代码地址:barragedemo.rar 。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我.

原文链接:http://www.jianshu.com/p/be53cbf4590c 。

最后此篇关于ios弹幕高效加载实现方式实例代码的文章就讲到这里了,如果你想了解更多关于ios弹幕高效加载实现方式实例代码的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com