iOS 弹幕制作

离职的最后一天,在公司学习下弹幕的制作.基于OC.

主要思路:

1.首先建一个弹幕类BulletView,基于UIView,然后在该类上写个UIlabel,用于放置弹幕文字,然后前端放置一个UIImageView,放置用户头像.该类主要绘制UI和动画.

2.其次建立一个弹幕的管理类BulletManager,主要管理弹幕数据源,随机分配弹幕轨迹,根据不同状态(start,enter,end)做不同处理,该类主要负责逻辑部分.

其中,在弹幕类BulletView中写一个回调,负责回调当前弹幕的状态(start,enter,end)给管理类BulletManager;在管理类BulletManage写一个回调,负责回调弹幕视图给ViewController.

弹幕类:

BulletView.h

 1 //
 2 //  BulletView.h
 3 //  danMu
 4 //
 5 //  Created by Shaoting Zhou on 2017/9/11.
 6 //  Copyright © 2017年 Shaoting Zhou. All rights reserved.
 7 //
 8
 9 #import <UIKit/UIKit.h>
10 typedef NS_ENUM(NSInteger,MoveStatus){
11     Start,
12     Enter,
13     End,
14 };
15 @interface BulletView : UIView
16 @property (nonatomic,assign) int trajectory;  //弹幕弹道
17 @property (nonatomic,copy) void(^ moveStatusBlock)(MoveStatus status);  //弹幕状态回调 开始  运行中 结束
18
19 -(instancetype)initWithCommentDic:(NSDictionary *)dic;   //初始化弹幕
20
21 -(void)startAnimation;  //开始动画
22 -(void)stopAnimation; //结束动画
23
24 @end

BulletView.m

  1 //
  2 //  BulletView.m
  3 //  danMu
  4 //
  5 //  Created by Shaoting Zhou on 2017/9/11.
  6 //  Copyright © 2017年 Shaoting Zhou. All rights reserved.
  7 //
  8
  9 #import "BulletView.h"
 10
 11 #define padding 10
 12 #define imgHeight 30
 13 @interface BulletView()
 14 @property (nonatomic,strong) UILabel * lbComment;
 15 @property (nonatomic,strong) UIImageView * imgView;
 16
 17 @end
 18
 19 @implementation BulletView
 20
 21 //MARK: 初始化弹幕
 22 -(instancetype)initWithCommentDic:(NSDictionary *)dic{
 23     if(self = [super init]){
 24         self.layer.cornerRadius = 30/2;
 25
 26         CGFloat colorR = arc4random()%255;
 27         CGFloat colorG = arc4random()%255;
 28         CGFloat colorB = arc4random()%255;
 29         self.backgroundColor = [UIColor colorWithRed:colorR/255 green:colorG/255 blue:colorB/255 alpha:1.0];
 30
 31         //计算弹幕的实际宽度
 32         NSDictionary *attr = @{NSFontAttributeName:[UIFont systemFontOfSize:14]};
 33         NSString * comment = dic[@"danmu"];
 34         CGFloat width = [comment sizeWithAttributes:attr].width;
 35         self.bounds = CGRectMake(0, 0, width + 2 * padding + imgHeight , 30);
 36         self.lbComment.text = comment;
 37         self.lbComment.frame = CGRectMake(padding + imgHeight, 0, width, 30);
 38
 39         //头像
 40         self.imgView.frame = CGRectMake(-padding, -padding, imgHeight + padding,  imgHeight + padding);
 41         self.imgView.layer.cornerRadius =  (imgHeight + padding)/2;
 42         NSURL * url = [NSURL URLWithString:dic[@"userPhoto"]];
 43         self.imgView.image = [UIImage imageWithData:[NSData dataWithContentsOfURL:url]];;
 44
 45 //        NSLog(@"%@",comment);
 46     }
 47     return self;
 48 }
 49
 50 -(UILabel *)lbComment{
 51     if(!_lbComment){
 52         self.lbComment = [[UILabel alloc]initWithFrame:CGRectZero];
 53         self.lbComment.font = [UIFont systemFontOfSize:14];
 54         self.lbComment.textColor = [UIColor whiteColor];
 55         self.lbComment.textAlignment =  NSTextAlignmentCenter;
 56         [self addSubview:self.lbComment];
 57
 58     }
 59     return _lbComment;
 60 }
 61
 62 -(UIImageView *)imgView{
 63     if(!_imgView){
 64         self.imgView = [UIImageView new];
 65         self.imgView.clipsToBounds = YES;
 66         self.imgView.contentMode = UIViewContentModeScaleAspectFill;
 67         [self addSubview:self.imgView];
 68     }
 69     return _imgView;
 70 }
 71
 72
 73
 74 //MARK:开始动画
 75 -(void)startAnimation{
 76     CGFloat screenWidth = [UIScreen mainScreen].bounds.size.width;
 77     CGFloat duration = 4.0f;
 78     CGFloat wholeWidth = screenWidth + CGRectGetWidth(self.bounds);
 79
 80 //    弹幕开始
 81     if(self.moveStatusBlock){
 82         self.moveStatusBlock(Start);
 83     }
 84
 85     CGFloat speed = wholeWidth/duration;   // v =  s/t
 86     CGFloat enterDuration = CGRectGetWidth(self.bounds)/speed;  //完全进入屏幕所需时间
 87     [self performSelector:@selector(enterScreen) withObject:nil afterDelay:enterDuration];
 88
 89
 90
 91     //v = s/t   时间相同,弹幕越长,速度越快
 92     __block CGRect frame = self.frame;
 93     [UIView animateWithDuration:duration delay:0 options:UIViewAnimationOptionCurveLinear animations:^{
 94         frame.origin.x = -wholeWidth;
 95         self.frame = frame;
 96     } completion:^(BOOL finished) {
 97         [self removeFromSuperview] ;
 98
 99 //        回调状态
100         if(self.moveStatusBlock){
101             self.moveStatusBlock(End);
102         }
103
104     }];
105
106
107 }
108
109 //MARK:结束动画
110 -(void)stopAnimation{
111     [NSObject cancelPreviousPerformRequestsWithTarget:self];
112     [self.layer removeAllAnimations];
113     [self removeFromSuperview];
114 }
115
116 //MARK: 弹幕完全入屏幕调用
117 -(void)enterScreen{
118     if(self.moveStatusBlock){
119         self.moveStatusBlock(Enter);
120     }
121 }
122
123 @end

BulletManager.h

 1 //
 2 //  BulletManager.h
 3 //  danMu
 4 //
 5 //  Created by Shaoting Zhou on 2017/9/11.
 6 //  Copyright © 2017年 Shaoting Zhou. All rights reserved.
 7 //
 8
 9 #import <Foundation/Foundation.h>
10
11 @class BulletView;
12 @interface BulletManager : NSObject
13
14 @property (nonatomic,copy) void(^generateViewBlock)(BulletView* view);
15
16 -(void)start;
17 -(void)stop;
18 -(void)createBulletView:(NSDictionary *)commentDic trajectory:(int)trajectory;
19
20 @end

BulletManager.m

  1 //
  2 //  BulletManager.m
  3 //  danMu
  4 //
  5 //  Created by Shaoting Zhou on 2017/9/11.
  6 //  Copyright © 2017年 Shaoting Zhou. All rights reserved.
  7 //
  8
  9 #import "BulletManager.h"
 10 #import "BulletView.h"
 11
 12 @interface BulletManager()
 13 @property (nonatomic,strong) NSMutableArray * datasource;   //弹幕数据源
 14 @property (nonatomic,strong) NSMutableArray * bulletComments;  //弹幕使用过程中的数组变量
 15 @property (nonatomic,strong) NSMutableArray * bulletViews;  //存放弹幕view的数组变量
 16 @property BOOL stopAnimation;  //动画结束标示
 17 @end
 18
 19 @implementation BulletManager
 20
 21 -(instancetype)init{
 22     if(self = [super init]){
 23         self.stopAnimation = YES;
 24     }
 25     return self;
 26 }
 27
 28 -(void)start{
 29     if(!self.stopAnimation){
 30         return;
 31     }
 32     self.stopAnimation = NO;
 33     [self.bulletComments removeAllObjects];
 34     [self.bulletComments addObjectsFromArray:self.datasource];
 35
 36     [self initBulletComment];
 37
 38 }
 39
 40 -(void)stop{
 41     if(self.stopAnimation){
 42         return;
 43     }
 44     self.stopAnimation = YES;
 45
 46     [self.bulletViews enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
 47         BulletView * view = obj;
 48         [view stopAnimation];
 49         view = nil;
 50     }];
 51     [self.bulletViews removeAllObjects];
 52 }
 53
 54
 55 //MARK:初始化弹幕,随机分配弹幕轨迹
 56 -(void)initBulletComment{
 57     NSMutableArray *  trajectorys = [NSMutableArray arrayWithArray:@[@(0),@(1),@(2),@(3)]];
 58     for (int i = 0; i < 4; i++) {
 59         if(self.bulletComments.count > 0){
 60             //        通过随机数获取弹幕轨迹
 61             NSInteger  index = arc4random()%trajectorys.count;
 62             int trajectory = [[trajectorys objectAtIndex:index] intValue];
 63             [trajectorys removeObjectAtIndex:index];
 64
 65             //    从弹幕数组中取出弹幕数据
 66             NSDictionary * commentDic = [self.bulletComments firstObject];
 67             [self.bulletComments removeObjectAtIndex:0];
 68
 69             [self createBulletView:commentDic trajectory:trajectory];
 70         }
 71
 72     }
 73
 74 }
 75
 76
 77
 78 //MARK: 创建弹幕视图
 79 -(void)createBulletView:(NSDictionary *)commentDic trajectory:(int)trajectory {
 80     if(self.stopAnimation){
 81         return;
 82     }
 83     BulletView * bulletView = [[BulletView alloc]initWithCommentDic:commentDic];
 84 //    NSLog(@"%@",commentDic);
 85     bulletView.trajectory = trajectory;
 86     [self.bulletViews addObject:bulletView];
 87
 88     __weak typeof (bulletView) weakView = bulletView;
 89     __weak typeof(self) weakSelf = self;
 90     bulletView.moveStatusBlock = ^(MoveStatus status){
 91         if(weakSelf.stopAnimation){
 92             return;
 93         }
 94
 95         switch (status) {
 96             case Start:{
 97                 //                弹幕开始,将view加入到弹幕管理的变量bulletViews中
 98                 [weakSelf.bulletViews addObject:weakView];
 99                 break;
100             }
101             case Enter:{
102                 //                弹幕完全进入屏幕,判断是否还有弹幕,有的话则在该弹幕轨迹中创建弹幕视图
103                 NSDictionary * commentDic = [self nextComment];
104                 if(commentDic){
105                     [weakSelf createBulletView:commentDic trajectory:trajectory];  //递归即可
106                 }
107                 break;
108             }
109             case End:{
110 //            弹幕飞出屏幕后,从bulletViews删除,移除资源
111                 if([weakSelf.bulletViews containsObject:weakView]){
112                     [weakView stopAnimation];
113                     [weakSelf.bulletViews removeObject:weakView];
114                 }
115                 //已经木有弹幕了,循环播放
116                 if(weakSelf.bulletViews.count == 0){
117                     self.stopAnimation = YES;
118                     [weakSelf start];
119                 }
120                 break;
121             }
122             default:
123                 break;
124         }
125
126     };
127
128 //    回调view给viewControlller
129     if(self.generateViewBlock){
130         self.generateViewBlock(bulletView);
131     }
132
133 }
134
135 //MARK:  取出下一条弹幕
136 -(NSDictionary *)nextComment{
137     NSDictionary * commentDic = [self.bulletComments firstObject];
138     if(commentDic){
139         [self.bulletComments removeObjectAtIndex:0];
140     }
141     return commentDic;
142 }
143
144 -(NSMutableArray *)datasource{
145     if(!_datasource){
146         NSData * data = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"data" ofType:@"json"]];
147         NSArray * ary = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil];
148         self.datasource = [NSMutableArray arrayWithArray:ary];
149     }
150     return _datasource;
151 }
152
153 -(NSMutableArray *)bulletComments{
154     if(!_bulletComments){
155         self.bulletComments = [NSMutableArray array];
156     }
157     return _bulletComments;
158 }
159
160 -(NSMutableArray *)bulletViews{
161     if(!_bulletViews){
162         self.bulletViews = [NSMutableArray array];
163     }
164     return _bulletViews;
165 }
166
167
168 @end

数据源:

[
 {
   "userPhoto":"https://ws1.sinaimg.cn/large/610dc034ly1fiz4ar9pq8j20u010xtbk.jpg",
   "danmu":"城市套路深,我要回农村!!!"
 },
 {
    "userPhoto":"https://ws1.sinaimg.cn/large/610dc034ly1fis7dvesn6j20u00u0jt4.jpg",
    "danmu":"农村路更滑,人心更复杂~~"
 },
 {
 "userPhoto":"https://ws1.sinaimg.cn/large/610dc034ly1fiiiyfcjdoj20u00u0ju0.jpg",
 "danmu":"6666666666666"
 },
 {
 "userPhoto":"https://ws1.sinaimg.cn/large/610dc034gy1fi2okd7dtjj20u011h40b.jpg",
 "danmu":"要死,要死,要死~~~~~~~~~~~~~~~~~~"
 },
 {
 "userPhoto":"http://ww1.sinaimg.cn/large/610dc034ly1fhyeyv5qwkj20u00u0q56.jpg",
 "danmu":"前方高能预警"
 },
 {
 "userPhoto":"http://ww3.sinaimg.cn/large/610dc034jw1f5d36vpqyuj20zk0qo7fc.jpg",
 "danmu":"剧透死全家"
 },
 {
 "userPhoto":"http://7xi8d6.com1.z0.glb.clouddn.com/2017-03-07-003645.jpg",
 "danmu":"我是迟到的freestyle"
 },
 {
 "userPhoto":"http://ww1.sinaimg.cn/large/610dc034ly1fhyeyv5qwkj20u00u0q56.jpg",
 "danmu":"这个碗又大又圆,就像这个剧又污又刺激"
 },
 {
 "userPhoto":"http://ww1.sinaimg.cn/large/610dc034ly1fhyeyv5qwkj20u00u0q56.jpg",
 "danmu":"哈哈哈哈."
 },
 {
 "userPhoto":"https://ws1.sinaimg.cn/large/610dc034ly1fiz4ar9pq8j20u010xtbk.jpg",
 "danmu":"??????我的麒麟臂又发作"
 }
 ]

OK,上面就是弹幕的模块代码,接下来就可以把弹幕放置在任何你需要的地方了,如;列表.视频,音频,网页等.

然后,在ViewController中使用该弹幕即可.如下,写三个按钮(开始弹幕,结束弹幕,增加一条弹幕),然后写调用代码即可.

 1 //
 2 //  ViewController.m
 3 //  danMu
 4 //
 5 //  Created by Shaoting Zhou on 2017/9/11.
 6 //  Copyright © 2017年 Shaoting Zhou. All rights reserved.
 7 //
 8
 9 #import "ViewController.h"
10 #import "BulletManager.h"
11 #import "BulletView.h"
12 @interface ViewController ()
13 @property (nonatomic,strong) BulletManager * manager;
14 @property (nonatomic,strong) UITextField * text;
15 @end
16
17 @implementation ViewController
18
19 - (void)viewDidLoad {
20     [super viewDidLoad];
21     self.manager = [[BulletManager alloc]init];
22     __weak typeof(self) weakSelf = self;
23     self.manager.generateViewBlock = ^(BulletView *view) {
24         [weakSelf addBulletView:view];
25     };
26     CGFloat width = [UIScreen mainScreen].bounds.size.width;
27
28     UIButton * startBtn = [[UIButton alloc]initWithFrame:CGRectMake(0, 100, width/3, 40)];
29     [startBtn setTitleColor:[UIColor redColor] forState:UIControlStateNormal];
30     [startBtn setTitle:@"开始" forState:UIControlStateNormal];
31     [startBtn addTarget:self action:@selector(startAction) forControlEvents:UIControlEventTouchUpInside];
32     [self.view addSubview:startBtn];
33
34     UIButton * endBtn = [[UIButton alloc]initWithFrame:CGRectMake(width/3, 100, width/3, 40)];
35     [endBtn setTitleColor:[UIColor redColor] forState:UIControlStateNormal];
36     [endBtn setTitle:@"结束" forState:UIControlStateNormal];
37     [endBtn addTarget:self action:@selector(endAction) forControlEvents:UIControlEventTouchUpInside];
38     [self.view addSubview:endBtn];
39
40     UIButton * addDanmu = [[UIButton alloc]initWithFrame:CGRectMake(width * 2/3, 100, width/3, 40)];
41     [addDanmu setTitleColor:[UIColor redColor] forState:UIControlStateNormal];
42     [addDanmu setTitle:@"增加" forState:UIControlStateNormal];
43     [addDanmu addTarget:self action:@selector(addDanmuAction) forControlEvents:UIControlEventTouchUpInside];
44     [self.view addSubview:addDanmu];
45
46     self.text = [[UITextField alloc]initWithFrame:CGRectMake(0, 150, width, 40)];
47     [self.text setBorderStyle:UITextBorderStyleLine];
48     self.text.placeholder = @"增加一条弹幕";
49     [self.view addSubview:self.text];
50
51     // Do any additional setup after loading the view, typically from a nib.
52 }
53
54 -(void)startAction{
55     [self.manager start];
56 }
57
58 -(void)endAction{
59     [self.manager stop];
60 }
61
62 -(void)addDanmuAction{
63
64     NSDictionary * dic =  @{@"userPhoto":@"https://ws1.sinaimg.cn/large/610dc034ly1fiz4ar9pq8j20u010xtbk.jpg",@"danmu":self.text.text};
65     [self.manager createBulletView:dic trajectory:0];
66
67 }
68
69 -(void)addBulletView:(BulletView *)view{
70     CGFloat width = [UIScreen mainScreen].bounds.size.width;
71     view.frame = CGRectMake(width, 300 + view.trajectory * 60, CGRectGetWidth(view.bounds), CGRectGetHeight(view.bounds));
72     [self.view addSubview:view];
73
74     [view startAnimation];
75
76 }
77
78 - (void)didReceiveMemoryWarning {
79     [super didReceiveMemoryWarning];
80     // Dispose of any resources that can be recreated.
81 }
82
83
84 @end

截图如下:

                                

          

            

感谢慕课网

时间: 2024-11-13 01:55:26

iOS 弹幕制作的相关文章

iOS表格制作

由于项目上的需求,需要做一个表格出来,来显示流程状态.刚开始脑子一头雾水,没有一点思路,但是靠着自己的座右铭--"世上无难事,只怕有心人",克服了所有困难.好,不说了,讲正事. 制作表格,还是需要tableView来做. 1. 创建一个UIView对象 : UIView *tableViewHeadView=[[UIView alloc]initWithFrame:CGRectMake(0, 0, kCount*kWidth, kHeight)]; self.myHeadView=ta

iOS -证书制作

iOS证书制作攻略及配置注意事项 使用APICloud平台开发APP商用,首先得有开发者账号和各种证书,之后云编译打包正式版,上传到appstore审核上架.现在APICloud特别推出,证书申请和配置在控制台配置证书的注意事项的攻略,提供给大家. 云编译p12证书制作 生成certSigningRequest文件 如图,打开应用程序->实用工具->钥匙串访问 如图,选择从证书颁发机构请求证书 接下来填写邮件地址,选择存储到磁盘,点击继续 如图,保存文件到桌面. 制作p12证书 首先打开苹果开

js利用节点,封装函数进行简单弹幕制作

js简单弹幕制作(注:简单弹幕从右向左执行,除了数字字母之外,汉字执行会出现出框是排列向下成一排清空状态(如下图),不必担心,简单弹幕的问题 后续有说明和解决方案). 思路: 1.在输入框内输入值显示在显示区域里 2.发送完毕,输入框内清空 3.弹幕从右向左,且多行(可控) 4.性能优化处理(显示完毕清空弹幕,计时器) 效果图: 代码: <!DOCTYPE html><html> <head> <meta charset="UTF-8">

游戏制作之路:一个对我来说可实现的High-end的Mac/iOS游戏制作大概计划

对于学习一些东西,我比较习惯任务驱动式的学习,也就是说,要事先订好一个目标,要做什么东西,达到什么效果,然后根据自己了解的知识作一个可以实现这个目标的计划. 现在要学的是游戏制作,而且是High-end的3D游戏制作.如前文 游戏制作之路:游戏引擎选择.Mac下和Windows下UnrealEngine 4体验对比.文档及其他 所说的,我已经选择了UE4这个画面最顶级的引擎,它即使是在iOS上,画面也是最高端的,可以参考 Zen Garden 这个Demo.然后我也大概确认了可行性,现在要解决的

cocos2d 制作fnt 批量缩放png ios 批量制作图标 gamedevkit 支持windows, mac

0.制作了一个简单软件 这个软件有4个功能: 批量缩放png图片 根据美术给的数字小图等,制作fnt字体文件供cocos2d使用 批量制作ios 图标和android 图标(只需传入一张图片) 截图制作,传入5张IPhone6+的截图,生成IPhone5,IPhone6的截图(这个功能没什么用,不过当你个人开发制作20+个游戏的时候,就能省点力) 下面是具体功能介绍...当然这个软件暂定为收费. 1. cocos2d 制作fnt 文件 2.批量缩放png图片 我之前蛋疼地做了一个游戏,使用了两套

ios Framework 制作 的一个坑 Reason: image not found dyld: Library not loaded:

为什么会这样的?因为我们做的是动态库,在使用的时候需要额外加一个步骤,要把Framework同时添加到'Embedded Binaries'中 ... 详情 请见 http://www.cocoachina.com/ios/20141126/10322.html 为了防止连接失效 我把文字 随便 复制点过来 有没有写SDK或者要将一些常用的工具类做成Framework的经历? 你或许自己写脚本完成了这项工作,相信也有很多的人使用 iOS-Universal-Framework ,随着Xcode

iOS XCode7制作.Framework动态库和.a静态库的总结

一.开发SDK时的支持情况: OC语言制作动态库时,支持iOS8+:OC语言制作静态库,支持iOS7+. Swift语言制作动态库时,支持iOS8+;Swift不支持静态库. 对于SDK来说,支持情况非常重要.像我就是一开始就被坑了,我使用Swift开发动态库的方式提供SDK,所以只能支持到iOS8+.但这意味着所有使用我的SDK的客户的APP都必须到iOS8+,这就坑爹了. 所以假如需要支持iOS7的话,只有使用OC语言开发.a静态库的一条路. 二.开发动态库的简易过程: 开发动态库时,只要通

ios学习-制作一个浏览图片的Demo

一.项目要求:制作一个浏览图片的Demo,要求包含夜间模式,以及改变图片大小,能够显示不同的图片描述 二.开发步骤: 1.在storyboard上添加一个空白的View,然后添加”设置“按钮,添加image View,图片序号Label,图片描述Label,更改图片Slider控件. 2.编写sliderValueChanged方法 3.在storyboard再添加一个空白的View,在新增的View上面添加Switch控件,用于夜间模式,添加Slider控件,用于改变图片的大小. 4.编写se

iOS弹幕基本实现及原理介绍

最近做项目的时候需要实现弹幕这个功能, 虽然感觉实现起来也不是很复杂, 但是自己还是不想写, 因为毕竟现在视频app挺火的, 而且基本都有弹幕这个功能. 但在网上找时却发现没有太多可以选择的第三方, 所以最近写了个demo, 并且放在了github上了(点击查看) 其中实现我原理我是参考这篇博客(点击查看) 实现出来的效果: 弹幕类型: 支持从右往左滚动, 支持中间 顶部和底部 弹幕. 弹幕内容: 支持表情, 自定义内容文字大小及颜色 使用方法: 创建CFDanmakuView对象, 进行配置,