离职的最后一天,在公司学习下弹幕的制作.基于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. Git命令git update-index --assume-unchanged,忽略不想提交的文件(忽略跟踪)

    场景 我们在自己的私有测试分支上调试项目逻辑,给文件做了一些特定的修改,但是文件不想被git提交,不想执行git status命令时出现在modified列表里:再比如,我们本地的数据库和测试环境的数 ...

  2. What's the Python Launcher?

    Look! Python launcher 你可能在他处见到过这鬼东西,when you install or uninstall python, and so on.那么你肯定与我一样对这个鬼东西起 ...

  3. Virtual DOM 和 diff 算法

    virtual DOM : virtual DOM 用 js 模拟 DOM 结构,用 js 来对比前后变化,提高重绘性能. diff: 比如在 git 中,如果用命令  git diff xxx文件 ...

  4. asp.net mvc session锁问题

    一.会话状态Session Session用于服务器端状态管理,使用Session之后,每个客户端都可以将实际的数据保存在服务器上,对于每个客户端的数据,将会生成一个对应的唯一的key(保存在客户端) ...

  5. JS 单线程

    js单线程阻塞实例setTimeout(function () { while (true) { } }, 1000);setTimeout(function () { alert('end 2'); ...

  6. webapi研究说明

    首先定义公共的返回对象 /// <summary> /// 返回数据对象 /// </summary> public class ResponseItem<T> { ...

  7. 英语发音规则---I字母常见发音组合有哪些

    英语发音规则---I字母常见发音组合有哪些 一.总结 一句话总结: I/y在开音节中发/aɪ/,例如:bite /baɪt/ n. 咬 I/y在闭音节中发 /ɪ/,例如:clinic /'klɪnɪk ...

  8. hdu-5009 Paint Pearls DP+双向链表 with Map实现去重优化

    http://acm.hdu.edu.cn/showproblem.php?pid=5009 题目要求对空序列染成目标颜色序列,对一段序列染色的成本是不同颜色数的平方. 这题我们显然会首先想到用DP去 ...

  9. Servlet的5种方式实现表单提交

    http://www.cnblogs.com/zhanghaoliang/p/5622900.html

  10. 最近使用Navicat for MySQl访问远程mysql数据库,出现报错,显示“2003- Can't connect MySQL Server on 'localhost'(10038)“。

    优先考虑mysql数据库是否开启 1.先看报错窗口.   通过百度,最终找到的原因是:远程3306端口未对外开放. 于是下面进行远程3306端口开放操作.   首先远程连接服务器,点击“开始”-“管理 ...