iOS 弹幕制作
离职的最后一天,在公司学习下弹幕的制作.基于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 弹幕制作的更多相关文章
- iOS XCode7制作.Framework动态库和.a静态库的总结
一.开发SDK时的支持情况: OC语言制作动态库时,支持iOS8+:OC语言制作静态库,支持iOS7+. Swift语言制作动态库时,支持iOS8+;Swift不支持静态库. 对于SDK来说,支持情况 ...
- 游戏制作之路:一个对我来说可实现的High-end的Mac/iOS游戏制作大概计划
对于学习一些东西,我比较习惯任务驱动式的学习,也就是说,要事先订好一个目标,要做什么东西,达到什么效果,然后根据自己了解的知识作一个可以实现这个目标的计划. 现在要学的是游戏制作,而且是High-en ...
- iOS,Xcode7 制作Framework,含资源和界面
Xcode7 制作Framework 本文通过Demo方式介绍1)将含bundle和存代码编写界面打包进framework:2)将storyboard +assets.xcassets打包. (一) ...
- iOS:插件制作入门
本文将介绍创建一个Xcode4插件所需要的基本步骤以及一些常用的方法.请注意为Xcode创建插件并没有任何的官方支持,因此本文所描述的方法和提供的信息可能会随Apple在Xcode上做的变化而失效.另 ...
- 笔记-iOS弹幕(源码)实现原理解析
最近,读完今年的第三本书<大话移动APP测试 Android与iOS>,在读到陈晔前辈改变中国测试行业的决心时,内心无比激动,作为一名初生的开发人员,我可能还无法理解测试行业的本质,但他那 ...
- 【转】iOS弹幕库OCBarrage-如何hold住每秒5000条巨量弹幕
最近公司做新需求, 原来用的老弹幕库, 已经无法满足需要. 迫不得已自己写了一套弹幕库OCBarrage. 这套弹幕库轻量, 可拓展, 高度自定义, 超高性能, 简单易上手. 无论哪家公司软件的性能绝 ...
- ios证书制作与上架指南
项目开发完了,要上架 ios AppStore 记录一下经过,以及需要提前准备和预防的东西,以便下次省心! 一.首先要申请开发者账号: 账号按流程注册申请,当时申请了够10遍,总结以下经验: 1.申请 ...
- iOS Framework制作流程
1.新建工程选择iOS —> Cocoa Touch Framework 2.进入创建好的工程删除掉自带的工程同名头文件 3.添加所需文件 4.TARGETS —> Build Setti ...
- ios学习-制作一个浏览图片的Demo
一.项目要求:制作一个浏览图片的Demo,要求包含夜间模式,以及改变图片大小,能够显示不同的图片描述 二.开发步骤: 1.在storyboard上添加一个空白的View,然后添加”设置“按钮,添加im ...
随机推荐
- 用Java画简单验证码
以下是具体代码: package com.jinzhi.tes2; import java.awt.Color;import java.awt.Font;import java.awt.Graphic ...
- daily start
2019/4/26 1. to summerize the merge experience, about makefile, about compile error analysis. 2. loo ...
- Dart编程语言入门
Dart基础入门语法介绍,详细说明可以查看相关视频<Dart编程语言入门>. 变量与常量 变量 1.使用 var 声明变量,默认值为 null var a;//null a = 10; 2 ...
- jQuery 查找属性
jQuery 查找属性 示例: <div xsk='123' > <div xsk='456' > // 具有xsk属性的所有标签 $('[xsk]') // xsk属性等于1 ...
- oracle addm报告
可通过@?/rdbms/admin/addmrpt.sql生成ADDM报告 ADDM本身并不是很实用,抽象级别太高,用于初步判断系统配置/IO子系统是否合理和快速参考,一个报告截图如下: 任务 '任务 ...
- Oracle 参数文件spfile
pfile和spfile 概念 ORACLE中的参数文件是一个包含一系列参数以及参数对应值的操作系统文件,可以分为两种类型.它们是在数据库实例启动时候加载的,决定了数据库的物理结构.内存.数据库的限制 ...
- log4net架构、配置、使用
架构说明 架构说明 上图是官方文档的提供的代码组织. Log4net的核心组件有: Logger, Appender, Filter, Layout, Object Render, Logger介绍 ...
- SQL小汇总
SQL小汇总 1.对每个时段的数据进行统计2.查询时间条件(to_date)3.插入序列号和系统时间4.查询当天.7天内.30天内5.查询前后x小时.分钟.天.月.6.保留小数点后4位7.查询字段A中 ...
- SpringMVC 搭建
1. 新建一个项目 Paste_Image.png 我们用eclipse新建项目,选择Dynamic Web Project(动态的Web项目). 点击Next Paste_Image.png ...
- wrk 安装使用
==================== 安装 ====================https://github.com/wg/wrk/wiki sudo yum -y groupinstall ...