這篇文章將為大家詳細(xì)講解有關(guān)iOS中如何實(shí)現(xiàn)視頻直播彈幕功能,小編覺(jué)得挺實(shí)用的,因此分享給大家做個(gè)參考,希望大家閱讀完這篇文章后可以有所收獲。
察隅網(wǎng)站建設(shè)公司創(chuàng)新互聯(lián)建站,察隅網(wǎng)站設(shè)計(jì)制作,有大型網(wǎng)站制作公司豐富經(jīng)驗(yàn)。已為察隅超過(guò)千家提供企業(yè)網(wǎng)站建設(shè)服務(wù)。企業(yè)網(wǎng)站搭建\成都外貿(mào)網(wǎng)站建設(shè)公司要多少錢(qián),請(qǐng)找那個(gè)售后服務(wù)好的察隅做網(wǎng)站的公司定做!
以下就是全部?jī)?nèi)容:
1.彈幕的實(shí)現(xiàn)性分析
首先,從視覺(jué)上明確當(dāng)前彈幕所具有的功能
從屏幕右側(cè)滑入左側(cè),直至完全消失
不管是長(zhǎng)的彈幕,還是短的彈幕,速度一致(可能有的需求是依據(jù)彈幕長(zhǎng)度,調(diào)整速度)
有彈幕軌道,不是隨機(jī)產(chǎn)生的彈幕
彈幕不會(huì)進(jìn)行重疊
接下來(lái)從功能角度思考需要做什么
重用機(jī)制,類似tableView有一個(gè)重用池,每個(gè)彈幕就是一個(gè)cell,當(dāng)有彈幕發(fā)送的時(shí)候,如果當(dāng)前的重用池沒(méi)有控件,則創(chuàng)建一個(gè)新的控件,如果重用池里面有控件,則拿出這個(gè)控件,開(kāi)始做動(dòng)畫(huà),在動(dòng)畫(huà)結(jié)束后重新將該控件重歸重用池。
速度要求一致的話,需要考慮幾點(diǎn),首先如下圖所示,紅色代表彈幕起始位置,藍(lán)色代表彈幕終止位置,長(zhǎng)度代表它們的實(shí)際長(zhǎng)度。當(dāng)我們?cè)O(shè)定動(dòng)畫(huà)的時(shí)候,采用[UIView animationWithDuration.....]這個(gè)動(dòng)畫(huà),設(shè)定duration為3s的話那么彈幕1的速度為(屏幕寬度+彈幕1寬度)/3,彈幕2的速度為(屏幕寬度+彈幕2寬度)/3,因?yàn)閺椖?長(zhǎng)度大于彈幕1的長(zhǎng)度,所以彈幕2的速度大于彈幕1的速度。(對(duì)于依據(jù)彈幕長(zhǎng)度調(diào)整速度的需求來(lái)說(shuō),這里相對(duì)簡(jiǎn)單一些,不需要專門(mén)去計(jì)算速度,唯一麻煩的是需要考慮速度不一致帶來(lái)的重疊問(wèn)題)
2.開(kāi)始準(zhǔn)備
精通數(shù)學(xué)公式V=S/t (V代表速度,S代表路程,t代表時(shí)間)(*^__^*)
3.正式開(kāi)始
創(chuàng)建一個(gè)View,命名為BarrageView,以及存儲(chǔ)彈幕數(shù)據(jù)的對(duì)象BarrageModel
以下為BarrageModel.h的內(nèi)容,存儲(chǔ)彈幕的頭像,昵稱,和消息內(nèi)容
@interface BarrageModel : NSObject /** 用戶昵稱 */ @property(nonatomic,copy)NSString *userName; /** 消息內(nèi)容 */ @property(nonatomic,copy)NSString *userMsg; /** 用戶頭像 */ @property(nonatomic,copy)NSString *userHeadImageUrl; @end
接下來(lái)對(duì)BarrageView內(nèi)容進(jìn)行編輯,注釋已經(jīng)盡可能的詳細(xì),因此不多做介紹
在.h文件中
#import@class BarrageModel; @interface BarrageView : UIView /** * 記錄當(dāng)前最后一個(gè)彈幕View,通過(guò)這個(gè)View來(lái)計(jì)算是顯示在哪個(gè)彈幕軌道上 */ @property(nonatomic,retain) UIView *lastAnimateView; /** * 發(fā)送彈幕 * * @param msgModel 彈幕數(shù)據(jù)Model */ -(void)barrageSendMsg:(BarrageModel *)msgModel; @end
在.m文件中
#import@class BarrageModel; @interface BarrageView : UIView
/** * 記錄當(dāng)前最后一個(gè)彈幕View,通過(guò)這個(gè)View來(lái)計(jì)算是顯示在哪個(gè)彈幕軌道上 */ @property(nonatomic,retain) UIView *lastAnimateView; /** * 發(fā)送彈幕 * * @param msgModel 彈幕數(shù)據(jù)Model */ -(void)barrageSendMsg:(BarrageModel *)msgModel; @end 在.m文件中 #import "BarrageView.h" #import "BarrageModel.h" //屏幕的尺寸 #define SCREEN_FRAME [[UIScreen mainScreen] bounds] //屏幕的高度 #define SCREEN_HEIGHT CGRectGetHeight(SCREEN_FRAME) //屏幕的寬度 #define SCREEN_WIDTH CGRectGetWidth(SCREEN_FRAME)
@interface BarrageView() { CGFloat _minSpaceTime; /** 最小間距時(shí)間 */ } /** 數(shù)據(jù)源 */ @property (nonatomic,retain)NSMutableArray *dataArr;
/** 彈幕UI的重用池 */ @property (nonatomic,retain)NSMutableArray *resuingArr; @end @implementation BarrageView - (instancetype)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { [self setInterface]; } return self; } -(void)setInterface { //初始化彈幕數(shù)據(jù)源,以及重用池 self.dataArr = [NSMutableArray array]; self.resuingArr = [NSMutableArray array]; //創(chuàng)建第一個(gè)彈幕加入重用池作為備用 UIView *view = [self createUI]; [self.resuingArr addObject:view]; //設(shè)置彈幕數(shù)據(jù)的初始輪詢時(shí)間 _minSpaceTime = 1; //檢查是否可以取彈幕數(shù)據(jù)進(jìn)行動(dòng)畫(huà) [self checkStartAnimatiom]; } -(void)checkStartAnimatiom { //當(dāng)有數(shù)據(jù)信息的時(shí)候 if (self.dataArr.count>0) { if (self.resuingArr.count>0) { //當(dāng)重用池里面有備用的彈幕UI時(shí) //在重用池中,取出第一個(gè)彈幕UI UIView *view = [self.resuingArr firstObject]; [self.resuingArr removeObject:view]; //取出的這個(gè)彈幕UI開(kāi)始動(dòng)畫(huà) [self startAnimationWithView:view]; }else{ //當(dāng)重用池沒(méi)有備用的彈幕UI時(shí) //重新創(chuàng)建一個(gè)彈幕UI UIView *view = [self createUI]; //拿著這個(gè)彈幕UI開(kāi)始動(dòng)畫(huà) [self startAnimationWithView:view]; } } //延遲執(zhí)行,在主線程中不能調(diào)用sleep()進(jìn)行延遲執(zhí)行 //調(diào)用自身方法,構(gòu)成一個(gè)無(wú)限循環(huán),不停的輪詢檢查是否有彈幕數(shù)據(jù) dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(_minSpaceTime * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ [self checkStartAnimatiom]; }); } -(void)startAnimationWithView:(UIView *)view { //取出第一條數(shù)據(jù) BarrageModel *barrageModel = [self.dataArr firstObject]; //計(jì)算昵稱的長(zhǎng)度 CGSize nameSize = [barrageModel.userName boundingRectWithSize:CGSizeMake(CGFLOAT_MAX, 14) options:NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading attributes:@{ } context:nil].size; //計(jì)算消息的長(zhǎng)度 CGSize msgSize = [barrageModel.userMsg boundingRectWithSize:CGSizeMake(CGFLOAT_MAX, 14) options:NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading attributes:@{ NSFontAttributeName:[UIFont systemFontOfSize:14] } context:nil].size; UIImageView *headImageView; //頭像 UILabel *userNameLabel; //昵稱 UILabel *userMsgLabel; //消息內(nèi)容 //進(jìn)行賦值,寬度適應(yīng) for (UIView *subView in view.subviews) { if (subView.tag == 1000) { headImageView = (UIImageView *)subView; headImageView.image = [UIImage imageNamed:@""]; }else if (subView.tag == 1001){ userNameLabel = (UILabel *)subView; userNameLabel.text = barrageModel.userName; //重新設(shè)置名稱Label寬度 CGRect nameRect = userNameLabel.frame; nameRect.size.width = nameSize.width; userNameLabel.frame = nameRect; }else{ userMsgLabel = (UILabel *)subView; userMsgLabel.text = barrageModel.userMsg; //重新設(shè)置消息內(nèi)容Label寬度 CGRect msgRect = userMsgLabel.frame; msgRect.size.width = msgSize.width; userMsgLabel.frame = msgRect; } } //重新設(shè)置彈幕的總體寬度 = 頭像寬度 + 頭像左右兩側(cè)距離 + (如果名字寬度大于消息內(nèi)容寬度,以名字寬度為基準(zhǔn),如果名字寬度小于消息內(nèi)容寬度,以消息內(nèi)容寬度為基準(zhǔn)) view.frame = CGRectMake(SCREEN_WIDTH, 0, CGRectGetWidth(headImageView.frame) + 4 + (CGRectGetWidth(userNameLabel.frame)>CGRectGetWidth(userMsgLabel.frame)?CGRectGetWidth(userNameLabel.frame):CGRectGetWidth(userMsgLabel.frame)), CGRectGetHeight(self.frame)); //不管彈幕長(zhǎng)短,速度要求一致。 V(速度) 為固定值 = 100(可根據(jù)實(shí)際自己調(diào)整) // S = 屏幕寬度+彈幕的寬度 V = 100(可根據(jù)實(shí)際自己調(diào)整) // V(速度) = S(路程)/t(時(shí)間) -------> t(時(shí)間) = S(路程)/V(速度); CGFloat duration = (view.frame.size.width+SCREEN_WIDTH)/100; //最小間距運(yùn)行時(shí)間為:彈幕從屏幕外完全移入屏幕內(nèi)的時(shí)間 + 間距的時(shí)間 _minSpaceTime = (view.frame.size.width + 30)/100; //最后做動(dòng)畫(huà)的view _lastAnimateView = view; //彈幕UI開(kāi)始動(dòng)畫(huà) [UIView animateWithDuration:duration delay:0 options:UIViewAnimationOptionCurveLinear animations:^{ //運(yùn)行至左側(cè)屏幕外 CGRect frame = view.frame; view.frame = CGRectMake(-frame.size.width, 0, frame.size.width, frame.size.height); } completion:^(BOOL finished) { //動(dòng)畫(huà)結(jié)束重新回到右側(cè)初始位置 view.frame = CGRectMake(SCREEN_WIDTH, 0, 0, CGRectGetHeight(self.frame)); //重新加入重用池 [self.resuingArr addObject:view]; }]; //將這個(gè)彈幕數(shù)據(jù)移除 [self.dataArr removeObject:barrageModel]; } #pragma mark public method -(void)barrageSendMsg:(BarrageModel *)msgModel{ //添加彈幕數(shù)據(jù) [self.dataArr addObject:msgModel]; } #pragma mark 創(chuàng)建控件 -(UIView *)createUI { UIView *view = [[UIView alloc] initWithFrame:CGRectMake(SCREEN_WIDTH, 0, 0, CGRectGetHeight(self.frame))]; view.backgroundColor = [UIColor colorWithWhite:0 alpha:0.3]; UIImageView *headImageView = [[UIImageView alloc] initWithFrame:CGRectMake(2, 2, CGRectGetHeight(self.frame)-4, CGRectGetHeight(self.frame)-4)]; headImageView.layer.cornerRadius = headImageView.frame.size.width/2; headImageView.layer.masksToBounds = YES; headImageView.tag = 1000; headImageView.backgroundColor = [UIColor redColor]; [view addSubview:headImageView]; UILabel *userNameLabel = [[UILabel alloc] initWithFrame:CGRectMake(CGRectGetMaxX(headImageView.frame) + 2, 0, 0,14)]; userNameLabel.font = [UIFont systemFontOfSize:14]; userNameLabel.tag = 1001; [view addSubview:userNameLabel]; UILabel *userMsgLabel = [[UILabel alloc] initWithFrame:CGRectMake(CGRectGetMaxX(headImageView.frame)+2, CGRectGetMaxY(userNameLabel.frame), 0, 14)]; userMsgLabel.font = [UIFont systemFontOfSize:14]; userMsgLabel.tag = 1002; [view addSubview:userMsgLabel]; [self addSubview:view]; return view; }
最后在vc里面
#import "ViewController.h" #import "BarrageView.h" #import "BarrageModel.h" //屏幕的尺寸 #define SCREEN_FRAME [[UIScreen mainScreen] bounds] //屏幕的高度 #define SCREEN_HEIGHT CGRectGetHeight(SCREEN_FRAME) //屏幕的寬度 #define SCREEN_WIDTH CGRectGetWidth(SCREEN_FRAME) @interface ViewController () /** 第一個(gè)彈幕軌道 */ @property (nonatomic,retain)BarrageView *barrageViewOne; /** 第二個(gè)彈幕軌道 */ @property (nonatomic,retain)BarrageView *barrageViewTwo; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; //創(chuàng)建第一個(gè)彈幕軌道 _barrageViewOne = [[BarrageView alloc]initWithFrame:CGRectMake(0,200, SCREEN_WIDTH, 34)]; [self.view addSubview:_barrageViewOne]; //創(chuàng)建第二個(gè)彈幕軌道 _barrageViewTwo = [[BarrageView alloc]initWithFrame:CGRectMake(0,300, SCREEN_WIDTH, 34)]; [self.view addSubview:_barrageViewTwo]; } -(void)touchesBegan:(NSSet*)touches withEvent:(UIEvent *)event { NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(sendMessage) userInfo:nil repeats:YES]; [timer fire]; } -(void)sendMessage { BarrageModel *model = [[BarrageModel alloc]init]; model.userName = @[@"張三",@"李四",@"王五",@"趙六",@"七七",@"八八",@"九九",@"十十",@"十一",@"十二",@"十三",@"十四"][arc4random()%12]; model.userMsg = @[@"阿達(dá)個(gè)人",@"都是vsqe12qwe",@"勝多負(fù)少的凡人歌",@"委屈翁二群二",@"12312",@"熱帖柔荑花",@"發(fā)彼此彼此",@"OK潑墨",@"人體有圖圖",@"額外熱無(wú)若無(wú)",@"微軟將圍"][arc4random()%11]; //計(jì)算當(dāng)前做動(dòng)畫(huà)的彈幕UI的位置 CGFloat onePositon = _barrageViewOne.lastAnimateView.layer.presentationLayer.frame.size.width + _barrageViewOne.lastAnimateView.layer.presentationLayer.frame.origin.x; //計(jì)算當(dāng)前做動(dòng)畫(huà)的彈幕UI的位置 CGFloat twoPositon = _barrageViewTwo.lastAnimateView.layer.presentationLayer.frame.size.width + _barrageViewTwo.lastAnimateView.layer.presentationLayer.frame.origin.x; if ( onePositon < twoPositon ) { [_barrageViewOne barrageSendMsg:model]; }else{ [_barrageViewTwo barrageSendMsg:model]; } } @end
關(guān)于“iOS中如何實(shí)現(xiàn)視頻直播彈幕功能”這篇文章就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,使各位可以學(xué)到更多知識(shí),如果覺(jué)得文章不錯(cuò),請(qǐng)把它分享出去讓更多的人看到。