由于我们的需求是在图片上跑弹幕. 而且图片还要点击放大查看.
网上的demo都没法用. 于是就有了以下代码
// BulleView.h
#import "SprayBulleModel.h"
typedef NS_ENUM(NSUInteger, BulletDirection) {
BulletDirectionR2L = 1, // 右向左
BulletDirectionL2R = 2, // 左向右
};
typedef enum : NSUInteger {
MoveStatusStart,
MoveStatusEnter ,
MoveStatusEnd,
} MoveStatus;
@interface BulleView : UIView
@property (nonatomic, assign) NSInteger trajectory; //弹道
@property (nonatomic, assign) BulletDirection direction;
@property (nonatomic, copy) void(^moveStatusBlock)(MoveStatus status); //弹幕状态回调
- (instancetype)initWithParam:(ViewpointModel *)param;
- (void)startAnimation;
- (void)stopAnimation;
@end
// BulleView.m
#import "BulleView.h"
@interface BulleView ()
@property (nonatomic, strong) UIImageView * commentView;
@property (nonatomic, copy) NSString * comment;
@property (nonatomic, copy) NSString * headUrl;
@property (nonatomic, assign) NSInteger groupType;
@end
@implementation BulleView
- (instancetype)initWithParam:(ViewpointModel *)param
{
self = [super init];
if (self) {
self.comment = param.content;
self.headUrl = [NSString stringWithFormat:@"%@%@", param.baseURL, param.headUrl];
self.groupType = param.campType;
[self addSubview:self.commentView];
self.bounds = self.commentView.bounds;
}
return self;
}
#pragma mark ---- start 开始动画
- (void)startAnimation
{
CGFloat screenW = [[UIScreen mainScreen] bounds].size.width;
CGFloat duration = 8.0f;
CGFloat wholeWidth = screenW + CGRectGetWidth(self.bounds);
if (self.moveStatusBlock) {
self.moveStatusBlock(MoveStatusStart);
}
//控制速度
CGFloat speed = wholeWidth / duration;
CGFloat enterDuration = CGRectGetWidth(self.bounds) / speed;
[self performSelector:@selector(enterScreen) withObject:nil afterDelay:enterDuration];
__block CGRect frame = self.frame;
[UIView animateWithDuration:duration animations:^{
if (_direction == BulletDirectionR2L) {
frame.origin.x -= wholeWidth;
} else {
frame.origin.x += wholeWidth;
}
self.frame = frame;
} completion:^(BOOL finished) {
[self removeFromSuperview];
if (self.moveStatusBlock) {
self.moveStatusBlock(MoveStatusEnd);
}
}];
}
#pragma mark ---- stop 结束动画
- (void)stopAnimation
{
[NSObject cancelPreviousPerformRequestsWithTarget:self];//停止
[self.layer removeAllAnimations];
[self removeFromSuperview];
}
- (void)enterScreen
{
if (self.moveStatusBlock) {
self.moveStatusBlock(MoveStatusEnter );
}
}
#pragma mark ---- setter
- (void)setDirection:(BulletDirection)direction
{
_direction = direction;
}
- (NSString *)cutText
{
if (self.comment.length <= 12) {
return self.comment;
}
NSString * tempStr = [self.comment substringToIndex:12];
return tempStr;
}
#pragma mark ---- lazy 懒加载
/* 弹幕UI */
- (UIView *)commentView
{
if (!_commentView) {
_commentView.userInteractionEnabled = YES;
CGFloat labelW = [PublicFunc sizeOfTextFontSize:14
maxSize:CGSizeMake(CGFLOAT_MAX, 30)
spacing:0
text:[self cutText]].width;
_commentView = [[UIImageView alloc] init];
_commentView.frame = CGRectMake(0, 0, labelW + 55, 42);
UIImageView * headImageView = [[UIImageView alloc] init];
[headImageView sd_setImageWithURL:[NSURL URLWithString:self.headUrl]];
[headImageView.layer setCornerRadius:15];
[headImageView.layer setMasksToBounds:YES];
headImageView.frame = CGRectMake(7, 6, 30, 30);
[_commentView addSubview:headImageView];
UILabel * label = [[UILabel alloc]init];
label.text = self.comment;
label.font = SFONT(12);
[_commentView addSubview:label];
CGFloat labelX = CGRectGetMaxX(headImageView.frame) + 7;
label.frame = CGRectMake(labelX, 6, labelW, 30);
if (self.groupType == 2) {
_commentView.image = [UIImage imageNamed:@"blue_bg_normal"];
label.textColor = RGB(0x0e6ac7, 1);
} else {
_commentView.image = [UIImage imageNamed:@"red_bg_normal"];
label.textColor = RGB(0xe8110f, 1);
}
}
return _commentView;
}
// BulletManager.h
#import "SprayBulleModel.h"
@class BulleView;
@interface BulletManager : NSObject
@property (nonatomic, strong) NSMutableArray * dataSource; //弹幕的数据来源
@property (nonatomic, assign) NSInteger trackNumber; //弹道数量
@property (nonatomic, copy) void(^geenerateViewBlock)(BulleView * bulleView);
- (void)start;
- (void)stop;
@end
#import "BulletManager.h"
#import "BulleView.h"
@interface BulletManager ()
@property (nonatomic, strong) NSMutableArray * bulletComments; //过程中的数据
@property (nonatomic, strong) NSMutableArray * bulletViews; //存储弹幕view
@property (nonatomic, assign) BOOL bStopAnimation;
@end
@implementation BulletManager
- (instancetype)init
{
self = [super init];
if (self) {
self.bStopAnimation = YES;
self.trackNumber = self.trackNumber ? self.trackNumber : 3;
}
return self;
}
//随机分配弹道
- (void)initBulletComment
{
NSMutableArray * trajectorys = [NSMutableArray array];
for (int i = 0; i < self.trackNumber; i ++) {
[trajectorys addObject:@(i)];
}
for (int i = 0; i < trajectorys.count; i ++) {
if (self.bulletComments.count > 0) {
//通过随机送获取弹幕轨迹
NSInteger index = arc4random()%trajectorys.count;
int trajectory = [[trajectorys objectAtIndex:index] intValue];
[trajectorys removeObjectAtIndex:index];
//从弹幕中逐一取出弹幕数据
ViewpointModel * param = [self.bulletComments firstObject];
[self.bulletComments removeObjectAtIndex:0];
[self createBulletView:param trajectory:trajectory];
}
}
}
//创建弹幕view
- (void)createBulletView:(ViewpointModel *)param trajectory:(int)trajectory
{
if (self.bStopAnimation) return;
BulleView * bulleView = [[BulleView alloc] initWithParam:param];
bulleView.trajectory = trajectory;
[self.bulletViews addObject:bulleView];
__weak typeof (bulleView) weakView = bulleView;
__weak typeof (self) weakSelf = self;
bulleView.moveStatusBlock = ^(MoveStatus status) {
if (self.bStopAnimation) return;
switch (status) {
case MoveStatusStart:
//弹幕开始进入屏幕
[weakSelf.bulletViews addObject:weakView];
break;
case MoveStatusEnter:
{
//弹幕完全进入屏幕, 判断是否还有其他内容, 如果有, 在该轨道再创建一个弹幕
ViewpointModel * param = [weakSelf nextComment];
if (param) {
[weakSelf createBulletView:param trajectory:trajectory];
}
}
break;
case MoveStatusEnd:
//移除并是个资源
if ([weakSelf.bulletViews containsObject:weakView]) {
[weakView stopAnimation];
[weakSelf.bulletViews removeObject:weakView];
}
if (weakSelf.bulletViews.count == 0) {
//已经没有弹幕, 开始循环
weakSelf.bStopAnimation = YES;
[weakSelf start];
}
break;
}
};
if (self.geenerateViewBlock) {
self.geenerateViewBlock(bulleView);
}
}
//从数据源中取下一条弹幕
- (ViewpointModel *)nextComment
{
if (self.bulletComments.count == 0) return nil;
ViewpointModel * param = [self.bulletComments firstObject];
if (param) {
[self.bulletComments removeObjectAtIndex:0];
}
return param;
}
- (void)start
{
if (!self.bStopAnimation) return;
self.bStopAnimation = NO;
[self.bulletComments removeAllObjects];
[self.bulletComments addObjectsFromArray:self.dataSource];
[self initBulletComment];
}
- (void)stop
{
if (self.bStopAnimation) return;
self.bStopAnimation = YES;
// [self.bulletViews enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
// BulleView * bulleView = obj;
// [bulleView stopAnimation];
// bulleView = nil;
// }];
// [self.bulletViews removeAllObjects];
}
#pragma mark ---- lazy
- (NSMutableArray *)dataSource
{
if (!_dataSource) {
_dataSource = [NSMutableArray array];
}
return _dataSource;
}
- (NSMutableArray *)bulletComments
{
if (!_bulletComments) {
_bulletComments = [NSMutableArray array];
}
return _bulletComments;
}
- (NSMutableArray *)bulletViews
{
if (!_bulletViews) {
_bulletViews = [NSMutableArray array];
}
return _bulletViews;
}
@end
- (BulletManager *)rightManager
{
if (!_rightManager) {
_rightManager = [[BulletManager alloc] init];
_rightManager.trackNumber = 1;
__weak typeof (self) weakself = self;
_rightManager.geenerateViewBlock = ^(BulleView * bulleView) {
bulleView.frame = CGRectMake(MainScreen_Width, [weakself bulleViewY:BulletDirectionR2L], CGRectGetWidth(bulleView.bounds), CGRectGetHeight(bulleView.bounds));
[weakself addBulletView:bulleView direction:BulletDirectionR2L];
};
}
return _rightManager;
}
这是一个从右向左的弹幕, 从左向右的起始x坐标应更改为
-CGRectGetWidth(bulleView.bounds)