iOS_一个简单的弹幕实现

由于我们的需求是在图片上跑弹幕. 而且图片还要点击放大查看.  

网上的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;
}

UI部分大致就算是完成了,  接下来的任务就是控制弹幕

//  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

剩下的步骤就是将弹幕一条一条的添加到需要显示的view 上

- (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)


这样一个在图片上的弹幕就好了. 


你可能感兴趣的:(iOS_一个简单的弹幕实现)