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

主要思路:

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

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

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

弹幕类:

BulletView.h

 //
// BulletView.h
// danMu
//
// Created by Shaoting Zhou on 2017/9/11.
// Copyright © 2017年 Shaoting Zhou. All rights reserved.
// #import <UIKit/UIKit.h>
typedef NS_ENUM(NSInteger,MoveStatus){
Start,
Enter,
End,
};
@interface BulletView : UIView
@property (nonatomic,assign) int trajectory; //弹幕弹道
@property (nonatomic,copy) void(^ moveStatusBlock)(MoveStatus status); //弹幕状态回调 开始 运行中 结束 -(instancetype)initWithCommentDic:(NSDictionary *)dic; //初始化弹幕 -(void)startAnimation; //开始动画
-(void)stopAnimation; //结束动画 @end

BulletView.m

 //
// BulletView.m
// danMu
//
// Created by Shaoting Zhou on 2017/9/11.
// Copyright © 2017年 Shaoting Zhou. All rights reserved.
// #import "BulletView.h" #define padding 10
#define imgHeight 30
@interface BulletView()
@property (nonatomic,strong) UILabel * lbComment;
@property (nonatomic,strong) UIImageView * imgView; @end @implementation BulletView //MARK: 初始化弹幕
-(instancetype)initWithCommentDic:(NSDictionary *)dic{
if(self = [super init]){
self.layer.cornerRadius = /; CGFloat colorR = arc4random()%;
CGFloat colorG = arc4random()%;
CGFloat colorB = arc4random()%;
self.backgroundColor = [UIColor colorWithRed:colorR/ green:colorG/ blue:colorB/ alpha:1.0]; //计算弹幕的实际宽度
NSDictionary *attr = @{NSFontAttributeName:[UIFont systemFontOfSize:]};
NSString * comment = dic[@"danmu"];
CGFloat width = [comment sizeWithAttributes:attr].width;
self.bounds = CGRectMake(, , width + * padding + imgHeight , );
self.lbComment.text = comment;
self.lbComment.frame = CGRectMake(padding + imgHeight, , width, ); //头像
self.imgView.frame = CGRectMake(-padding, -padding, imgHeight + padding, imgHeight + padding);
self.imgView.layer.cornerRadius = (imgHeight + padding)/;
NSURL * url = [NSURL URLWithString:dic[@"userPhoto"]];
self.imgView.image = [UIImage imageWithData:[NSData dataWithContentsOfURL:url]];; // NSLog(@"%@",comment);
}
return self;
} -(UILabel *)lbComment{
if(!_lbComment){
self.lbComment = [[UILabel alloc]initWithFrame:CGRectZero];
self.lbComment.font = [UIFont systemFontOfSize:];
self.lbComment.textColor = [UIColor whiteColor];
self.lbComment.textAlignment = NSTextAlignmentCenter;
[self addSubview:self.lbComment]; }
return _lbComment;
} -(UIImageView *)imgView{
if(!_imgView){
self.imgView = [UIImageView new];
self.imgView.clipsToBounds = YES;
self.imgView.contentMode = UIViewContentModeScaleAspectFill;
[self addSubview:self.imgView];
}
return _imgView;
} //MARK:开始动画
-(void)startAnimation{
CGFloat screenWidth = [UIScreen mainScreen].bounds.size.width;
CGFloat duration = 4.0f;
CGFloat wholeWidth = screenWidth + CGRectGetWidth(self.bounds); // 弹幕开始
if(self.moveStatusBlock){
self.moveStatusBlock(Start);
} CGFloat speed = wholeWidth/duration; // v = s/t
CGFloat enterDuration = CGRectGetWidth(self.bounds)/speed; //完全进入屏幕所需时间
[self performSelector:@selector(enterScreen) withObject:nil afterDelay:enterDuration]; //v = s/t 时间相同,弹幕越长,速度越快
__block CGRect frame = self.frame;
[UIView animateWithDuration:duration delay: options:UIViewAnimationOptionCurveLinear animations:^{
frame.origin.x = -wholeWidth;
self.frame = frame;
} completion:^(BOOL finished) {
[self removeFromSuperview] ; // 回调状态
if(self.moveStatusBlock){
self.moveStatusBlock(End);
} }]; } //MARK:结束动画
-(void)stopAnimation{
[NSObject cancelPreviousPerformRequestsWithTarget:self];
[self.layer removeAllAnimations];
[self removeFromSuperview];
} //MARK: 弹幕完全入屏幕调用
-(void)enterScreen{
if(self.moveStatusBlock){
self.moveStatusBlock(Enter);
}
} @end

BulletManager.h

 //
// BulletManager.h
// danMu
//
// Created by Shaoting Zhou on 2017/9/11.
// Copyright © 2017年 Shaoting Zhou. All rights reserved.
// #import <Foundation/Foundation.h> @class BulletView;
@interface BulletManager : NSObject @property (nonatomic,copy) void(^generateViewBlock)(BulletView* view); -(void)start;
-(void)stop;
-(void)createBulletView:(NSDictionary *)commentDic trajectory:(int)trajectory; @end

BulletManager.m

 //
// BulletManager.m
// danMu
//
// Created by Shaoting Zhou on 2017/9/11.
// Copyright © 2017年 Shaoting Zhou. All rights reserved.
// #import "BulletManager.h"
#import "BulletView.h" @interface BulletManager()
@property (nonatomic,strong) NSMutableArray * datasource; //弹幕数据源
@property (nonatomic,strong) NSMutableArray * bulletComments; //弹幕使用过程中的数组变量
@property (nonatomic,strong) NSMutableArray * bulletViews; //存放弹幕view的数组变量
@property BOOL stopAnimation; //动画结束标示
@end @implementation BulletManager -(instancetype)init{
if(self = [super init]){
self.stopAnimation = YES;
}
return self;
} -(void)start{
if(!self.stopAnimation){
return;
}
self.stopAnimation = NO;
[self.bulletComments removeAllObjects];
[self.bulletComments addObjectsFromArray:self.datasource]; [self initBulletComment]; } -(void)stop{
if(self.stopAnimation){
return;
}
self.stopAnimation = YES; [self.bulletViews enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
BulletView * view = obj;
[view stopAnimation];
view = nil;
}];
[self.bulletViews removeAllObjects];
} //MARK:初始化弹幕,随机分配弹幕轨迹
-(void)initBulletComment{
NSMutableArray * trajectorys = [NSMutableArray arrayWithArray:@[@(),@(),@(),@()]];
for (int i = ; i < ; i++) {
if(self.bulletComments.count > ){
// 通过随机数获取弹幕轨迹
NSInteger index = arc4random()%trajectorys.count;
int trajectory = [[trajectorys objectAtIndex:index] intValue];
[trajectorys removeObjectAtIndex:index]; // 从弹幕数组中取出弹幕数据
NSDictionary * commentDic = [self.bulletComments firstObject];
[self.bulletComments removeObjectAtIndex:]; [self createBulletView:commentDic trajectory:trajectory];
} } } //MARK: 创建弹幕视图
-(void)createBulletView:(NSDictionary *)commentDic trajectory:(int)trajectory {
if(self.stopAnimation){
return;
}
BulletView * bulletView = [[BulletView alloc]initWithCommentDic:commentDic];
// NSLog(@"%@",commentDic);
bulletView.trajectory = trajectory;
[self.bulletViews addObject:bulletView]; __weak typeof (bulletView) weakView = bulletView;
__weak typeof(self) weakSelf = self;
bulletView.moveStatusBlock = ^(MoveStatus status){
if(weakSelf.stopAnimation){
return;
} switch (status) {
case Start:{
// 弹幕开始,将view加入到弹幕管理的变量bulletViews中
[weakSelf.bulletViews addObject:weakView];
break;
}
case Enter:{
// 弹幕完全进入屏幕,判断是否还有弹幕,有的话则在该弹幕轨迹中创建弹幕视图
NSDictionary * commentDic = [self nextComment];
if(commentDic){
[weakSelf createBulletView:commentDic trajectory:trajectory]; //递归即可
}
break;
}
case End:{
// 弹幕飞出屏幕后,从bulletViews删除,移除资源
if([weakSelf.bulletViews containsObject:weakView]){
[weakView stopAnimation];
[weakSelf.bulletViews removeObject:weakView];
}
//已经木有弹幕了,循环播放
if(weakSelf.bulletViews.count == ){
self.stopAnimation = YES;
[weakSelf start];
}
break;
}
default:
break;
} }; // 回调view给viewControlller
if(self.generateViewBlock){
self.generateViewBlock(bulletView);
} } //MARK: 取出下一条弹幕
-(NSDictionary *)nextComment{
NSDictionary * commentDic = [self.bulletComments firstObject];
if(commentDic){
[self.bulletComments removeObjectAtIndex:];
}
return commentDic;
} -(NSMutableArray *)datasource{
if(!_datasource){
NSData * data = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"data" ofType:@"json"]];
NSArray * ary = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil];
self.datasource = [NSMutableArray arrayWithArray:ary];
}
return _datasource;
} -(NSMutableArray *)bulletComments{
if(!_bulletComments){
self.bulletComments = [NSMutableArray array];
}
return _bulletComments;
} -(NSMutableArray *)bulletViews{
if(!_bulletViews){
self.bulletViews = [NSMutableArray array];
}
return _bulletViews;
} @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":""
},
{
"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":"

iOS 弹幕制作的更多相关文章

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

    一.开发SDK时的支持情况: OC语言制作动态库时,支持iOS8+:OC语言制作静态库,支持iOS7+. Swift语言制作动态库时,支持iOS8+;Swift不支持静态库. 对于SDK来说,支持情况 ...

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

    对于学习一些东西,我比较习惯任务驱动式的学习,也就是说,要事先订好一个目标,要做什么东西,达到什么效果,然后根据自己了解的知识作一个可以实现这个目标的计划. 现在要学的是游戏制作,而且是High-en ...

  3. iOS,Xcode7 制作Framework,含资源和界面

    Xcode7 制作Framework  本文通过Demo方式介绍1)将含bundle和存代码编写界面打包进framework:2)将storyboard +assets.xcassets打包. (一) ...

  4. iOS:插件制作入门

    本文将介绍创建一个Xcode4插件所需要的基本步骤以及一些常用的方法.请注意为Xcode创建插件并没有任何的官方支持,因此本文所描述的方法和提供的信息可能会随Apple在Xcode上做的变化而失效.另 ...

  5. 笔记-iOS弹幕(源码)实现原理解析

    最近,读完今年的第三本书<大话移动APP测试 Android与iOS>,在读到陈晔前辈改变中国测试行业的决心时,内心无比激动,作为一名初生的开发人员,我可能还无法理解测试行业的本质,但他那 ...

  6. 【转】iOS弹幕库OCBarrage-如何hold住每秒5000条巨量弹幕

    最近公司做新需求, 原来用的老弹幕库, 已经无法满足需要. 迫不得已自己写了一套弹幕库OCBarrage. 这套弹幕库轻量, 可拓展, 高度自定义, 超高性能, 简单易上手. 无论哪家公司软件的性能绝 ...

  7. ios证书制作与上架指南

    项目开发完了,要上架 ios AppStore 记录一下经过,以及需要提前准备和预防的东西,以便下次省心! 一.首先要申请开发者账号: 账号按流程注册申请,当时申请了够10遍,总结以下经验: 1.申请 ...

  8. iOS Framework制作流程

    1.新建工程选择iOS —> Cocoa Touch Framework 2.进入创建好的工程删除掉自带的工程同名头文件 3.添加所需文件 4.TARGETS —> Build Setti ...

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

    一.项目要求:制作一个浏览图片的Demo,要求包含夜间模式,以及改变图片大小,能够显示不同的图片描述 二.开发步骤: 1.在storyboard上添加一个空白的View,然后添加”设置“按钮,添加im ...

随机推荐

  1. java static语句的总结

    static 是静态方法,他的引用不需要对象,可以使用类名直接进行引用,当然也不需要this.      由于不需要对象,所以static方法内无法调用非static的方法或对象   至于为什么mai ...

  2. Eclipse新建Java工程出现红色感叹号怎么解决?

    安装了新版本的JDK之后,在Eclipse中新建Java工程出现红色感叹号怎么解决? 其实只要在Eclipse中重新设置一下JDK路径就行了 路径:右键Java工程>>Build Path ...

  3. win7下MKVToolNix进行mkv字幕封装

    MKVToolNix下载地址(https://www.fosshub.com/MKVToolNix.html) 下载安装后打开,当时没创建桌面图标,GUI地址(C:\ProgramData\Micro ...

  4. android中SELINUX规则分析和语法简介【转】

    本文转载自:https://blog.csdn.net/LoongEmbedded/article/details/62430039 1. SELINUX是可以理解为一种Android上面的安全机制, ...

  5. 廖雪峰JavaScript学习笔记(基础及数据类型、变量)

    先睹为快 alert('我要学JavaScript!'); Run: 基本语法: 1.每个语句以;结束,不强制 2.语句块用{...} 3.//单行注释,/*...*/ 多行注释 数据类型: 1.不区 ...

  6. RNN流程

    1.记号 2.前向计算,第二张图是第一张图的公式的简化.其中a称之为隐状态 3.计算代价函数

  7. SQL LITE安装

    SQLite是一款轻型的嵌入式关系数据库,轻量级,效率高,操作起来也特别方便我们今天来讲解一下SQLite的安装和一些基本操作SQLite下载我是64位机,下载下面的两个解压就好添加path环境变量, ...

  8. ionic3开发ios端

    ionic框架是一端开发,三端适用(android端,ios端,web端),意思是在其中一端开发的代码,拿到另外两端,代码同样生效 那现在就说一下在web端开发拿到ios端开发前需要做的事情 开发io ...

  9. js之原型,原型链

    1.原型是什么?    在构造函数创建出来的时候,系统会默认的创建并关联一个对象,这个对象就是原型,原型对象默认是空对象    默认的原型对象中会有一个属性constructor指向该构造函数  原型 ...

  10. 移动端解决悬浮层(悬浮header、footer)会遮挡住内容的方法

    固定Footer Bootstrap框架提供了两种固定导航条的方式: ☑  .navbar-fixed-top:导航条固定在浏览器窗口顶部 ☑  .navbar-fixed-bottom:导航条固定在 ...