离职的最后一天,在公司学习下弹幕的制作.基于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. List添加map,后添加的map覆盖前面的问题

    List resultList = new ArrayList(); Map map = new HashMap(); while(rs.next()){ String userid = rs.get ...

  2. LR访问Https接口

    实操篇 第一步:需要跟开发或者运维要到要访问的https接口的证书(有关证书的问题我们在原理中有解释). 第二步:确定要来的证书的格式是否为pem格式的.首先,LR只能够识别pem格式的证书而且是DE ...

  3. 2015全国大学生数学建模B题浅谈

    题目请自主上网获取. 分析下思路.第一问,不同时空的出租车的“供求匹配”程度. 也就是说要选取的数据要有时间和地理两个维度.实体对象是出租车.关键的问题就是地点怎么选? 选择的城市具备如下经济较发达, ...

  4. nginx-fastcgi 第九章

    CGI全称通用网关接口 Commmon Gateway Interface 用于HTTP服务上的程序服务通信交流的一种工具,CGI程序须运行在网络服务器上. 传统CGI接口方式性能较差,由于每次HTT ...

  5. org.springframework.beans.TypeMismatchException: Failed to convert property value of type 'null' to required type 'double' for property 'band'; nested exception is org.springframework.core.convert.Con

    本文为博主原创,未经允许不得转载: 先将异常粘贴出来: 20:37:26,909 ERROR [com.suning.fucdn.controller.ProductDataStaticsContro ...

  6. 力扣(LeetCode)482. 密钥格式化

    给定一个密钥字符串S,只包含字母,数字以及 '-'(破折号).N 个 '-' 将字符串分成了 N+1 组.给定一个数字 K,重新格式化字符串,除了第一个分组以外,每个分组要包含 K 个字##符,第一个 ...

  7. pytest文档13-allure2生成html报告(史上最详细)

    前言 allure是一个report框架,支持java的Junit/testng等框架,当然也可以支持python的pytest框架,也可以集成到Jenkins上展示高大上的报告界面. 环境准备 1. ...

  8. linux基础11-bash编程(字符串测试 和 for循环)

    练习:传递一个用户名参数给脚本,判断此用户的用户名跟其基本组的组名是否一致,并将结果显示出来.(1)字符测试:==:测试是否相等,相等为真,不等为假!=: 测试是否不等,不等为真,等为假>< ...

  9. 【洛谷p1605】迷宫

    (还记得我昨天大概没人看到的博客(我删辽)吗qwq,2019.4.14下午交的qwq 那篇博客大致内容就是:我提交楼上这道题,交了好久好久好久好久 现在我告诉你,那次评测还N/A着呢qwq) tqlq ...

  10. C# 关键字this用法

    1.this代表当前类的实例对象 public class Test { public string Name{get;set;} public void TestChange(string strN ...