亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 系統 > iOS > 正文

代碼詳解iOS視頻直播彈幕功能

2020-07-26 02:35:33
字體:
來源:轉載
供稿:網友

本篇內容通過步驟詳細給大家講解了iOS視頻直播彈幕的原理以及實現代碼分析,以下就是全部內容:

1.彈幕的實現性分析

首先,從視覺上明確當前彈幕所具有的功能

從屏幕右側滑入左側,直至完全消失

不管是長的彈幕,還是短的彈幕,速度一致(可能有的需求是依據彈幕長度,調整速度)

有彈幕軌道,不是隨機產生的彈幕

彈幕不會進行重疊

接下來從功能角度思考需要做什么

重用機制,類似tableView有一個重用池,每個彈幕就是一個cell,當有彈幕發送的時候,如果當前的重用池沒有控件,則創建一個新的控件,如果重用池里面有控件,則拿出這個控件,開始做動畫,在動畫結束后重新將該控件重歸重用池。

速度要求一致的話,需要考慮幾點,首先如下圖所示,紅色代表彈幕起始位置,藍色代表彈幕終止位置,長度代表它們的實際長度。當我們設定動畫的時候,采用[UIView animationWithDuration.....]這個動畫,設定duration為3s的話那么彈幕1的速度為(屏幕寬度+彈幕1寬度)/3,彈幕2的速度為(屏幕寬度+彈幕2寬度)/3,因為彈幕2長度大于彈幕1的長度,所以彈幕2的速度大于彈幕1的速度。(對于依據彈幕長度調整速度的需求來說,這里相對簡單一些,不需要專門去計算速度,唯一麻煩的是需要考慮速度不一致帶來的重疊問題)

2.開始準備

精通數學公式V=S/t (V代表速度,S代表路程,t代表時間)(*^__^*) 

3.正式開始

創建一個View,命名為BarrageView,以及存儲彈幕數據的對象BarrageModel

以下為BarrageModel.h的內容,存儲彈幕的頭像,昵稱,和消息內容

@interface BarrageModel : NSObject/** 用戶昵稱 */@property(nonatomic,copy)NSString *userName;/** 消息內容 */@property(nonatomic,copy)NSString *userMsg;/** 用戶頭像 */@property(nonatomic,copy)NSString *userHeadImageUrl;@end

接下來對BarrageView內容進行編輯,注釋已經盡可能的詳細,因此不多做介紹

在.h文件中

#import <UIKit/UIKit.h>@class BarrageModel;@interface BarrageView : UIView/** * 記錄當前最后一個彈幕View,通過這個View來計算是顯示在哪個彈幕軌道上 */@property(nonatomic,retain) UIView *lastAnimateView;/** * 發送彈幕 * * @param msgModel 彈幕數據Model */-(void)barrageSendMsg:(BarrageModel *)msgModel;@end

在.m文件中

#import <UIKit/UIKit.h>
@class BarrageModel;
@interface BarrageView : UIView
/**
 * 記錄當前最后一個彈幕View,通過這個View來計算是顯示在哪個彈幕軌道上
 */
@property(nonatomic,retain) UIView *lastAnimateView;
/**
 * 發送彈幕
 *
 * @param msgModel 彈幕數據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; /** 最小間距時間 */
}
/** 數據源 */
@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
{
  //初始化彈幕數據源,以及重用池
  self.dataArr = [NSMutableArray array];
  self.resuingArr = [NSMutableArray array];
  //創建第一個彈幕加入重用池作為備用
  UIView *view = [self createUI];
  [self.resuingArr addObject:view];
  //設置彈幕數據的初始輪詢時間
  _minSpaceTime = 1;
  //檢查是否可以取彈幕數據進行動畫
  [self checkStartAnimatiom];
}
-(void)checkStartAnimatiom
{
  //當有數據信息的時候
  if (self.dataArr.count>0) {
    if (self.resuingArr.count>0) { //當重用池里面有備用的彈幕UI時
     
      //在重用池中,取出第一個彈幕UI
      UIView *view = [self.resuingArr firstObject];
      [self.resuingArr removeObject:view];
      //取出的這個彈幕UI開始動畫
      [self startAnimationWithView:view];
     
    }else{ //當重用池沒有備用的彈幕UI時
     
      //重新創建一個彈幕UI
      UIView *view = [self createUI];
      //拿著這個彈幕UI開始動畫
      [self startAnimationWithView:view];
    }
  }
  //延遲執行,在主線程中不能調用sleep()進行延遲執行
  //調用自身方法,構成一個無限循環,不停的輪詢檢查是否有彈幕數據
  dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(_minSpaceTime * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    [self checkStartAnimatiom];
  });
}
-(void)startAnimationWithView:(UIView *)view
{
  //取出第一條數據
  BarrageModel *barrageModel = [self.dataArr firstObject];
  //計算昵稱的長度
  CGSize nameSize = [barrageModel.userName boundingRectWithSize:CGSizeMake(CGFLOAT_MAX, 14) options:NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading attributes:@{                                                                         } context:nil].size;
  //計算消息的長度
  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;   //消息內容
  //進行賦值,寬度適應
  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;
      //重新設置名稱Label寬度
      CGRect nameRect = userNameLabel.frame;
      nameRect.size.width = nameSize.width;
      userNameLabel.frame = nameRect;
    }else{
      userMsgLabel = (UILabel *)subView;
      userMsgLabel.text = barrageModel.userMsg;
      //重新設置消息內容Label寬度
      CGRect msgRect = userMsgLabel.frame;
      msgRect.size.width = msgSize.width;
      userMsgLabel.frame = msgRect;
    }
  }
  //重新設置彈幕的總體寬度 = 頭像寬度 + 頭像左右兩側距離 + (如果名字寬度大于消息內容寬度,以名字寬度為基準,如果名字寬度小于消息內容寬度,以消息內容寬度為基準)
  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));
  //不管彈幕長短,速度要求一致。 V(速度) 為固定值 = 100(可根據實際自己調整)
  // S = 屏幕寬度+彈幕的寬度 V = 100(可根據實際自己調整)
  // V(速度) = S(路程)/t(時間) -------> t(時間) = S(路程)/V(速度);
  CGFloat duration = (view.frame.size.width+SCREEN_WIDTH)/100;
  //最小間距運行時間為:彈幕從屏幕外完全移入屏幕內的時間 + 間距的時間
  _minSpaceTime = (view.frame.size.width + 30)/100;
  //最后做動畫的view
  _lastAnimateView = view; 
  //彈幕UI開始動畫
  [UIView animateWithDuration:duration delay:0 options:UIViewAnimationOptionCurveLinear animations:^{
    //運行至左側屏幕外
    CGRect frame = view.frame;
    view.frame = CGRectMake(-frame.size.width, 0, frame.size.width, frame.size.height);
  } completion:^(BOOL finished) {
    //動畫結束重新回到右側初始位置
    view.frame = CGRectMake(SCREEN_WIDTH, 0, 0, CGRectGetHeight(self.frame));
    //重新加入重用池
    [self.resuingArr addObject:view];
  }]; 
  //將這個彈幕數據移除
  [self.dataArr removeObject:barrageModel];
}
#pragma mark public method
-(void)barrageSendMsg:(BarrageModel *)msgModel{
  //添加彈幕數據
  [self.dataArr addObject:msgModel];
}
#pragma mark 創建控件
-(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"http://屏幕的尺寸#define SCREEN_FRAME [[UIScreen mainScreen] bounds]//屏幕的高度#define SCREEN_HEIGHT CGRectGetHeight(SCREEN_FRAME)//屏幕的寬度#define SCREEN_WIDTH CGRectGetWidth(SCREEN_FRAME)@interface ViewController ()/** 第一個彈幕軌道 */@property (nonatomic,retain)BarrageView *barrageViewOne;/** 第二個彈幕軌道 */@property (nonatomic,retain)BarrageView *barrageViewTwo;@end@implementation ViewController- (void)viewDidLoad { [super viewDidLoad]; //創建第一個彈幕軌道 _barrageViewOne = [[BarrageView alloc]initWithFrame:CGRectMake(0,200, SCREEN_WIDTH, 34)]; [self.view addSubview:_barrageViewOne]; //創建第二個彈幕軌道 _barrageViewTwo = [[BarrageView alloc]initWithFrame:CGRectMake(0,300, SCREEN_WIDTH, 34)]; [self.view addSubview:_barrageViewTwo];}-(void)touchesBegan:(NSSet<UITouch *> *)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 = @[@"阿達個人",@"都是vsqe12qwe",@"勝多負少的凡人歌",@"委屈翁二群二",@"12312",@"熱帖柔荑花",@"發彼此彼此",@"OK潑墨",@"人體有圖圖",@"額外熱無若無",@"微軟將圍"][arc4random()%11]; //計算當前做動畫的彈幕UI的位置 CGFloat onePositon = _barrageViewOne.lastAnimateView.layer.presentationLayer.frame.size.width + _barrageViewOne.lastAnimateView.layer.presentationLayer.frame.origin.x; //計算當前做動畫的彈幕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

4.測試結論

經一個小時的定時器測試,內存沒有增加。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
久久国产精品99国产精| 日韩在线观看你懂的| 久久精品国产亚洲一区二区| 成人免费大片黄在线播放| 韩国19禁主播vip福利视频| 精品国产一区二区三区久久久狼| 亚洲国产精品小视频| 久久精品国产亚洲一区二区| 亚洲最大的免费| 国产精品自拍偷拍视频| 久久久精品视频在线观看| 成人在线精品视频| 国产大片精品免费永久看nba| 91色精品视频在线| 亚洲国产高清自拍| 久久亚洲精品国产亚洲老地址| 中文字幕亚洲无线码在线一区| 亚洲欧美在线一区| 91精品国产综合久久香蕉的用户体验| 欧美久久久精品| 免费不卡欧美自拍视频| 欧美日韩国产成人| 91av在线播放视频| 国产精品久久999| 欧美激情18p| 国产成人福利网站| 亚洲free性xxxx护士hd| 久久不射电影网| 欧美日韩国产中文精品字幕自在自线| 国产精品福利在线| 国产精品色婷婷视频| 日韩精品视频在线观看网址| 日韩电影中文字幕一区| 日韩精品视频在线免费观看| 中文字幕v亚洲ⅴv天堂| 久久97久久97精品免视看| 日本三级韩国三级久久| xxav国产精品美女主播| 欧美极品在线播放| 欧美国产中文字幕| 中文字幕自拍vr一区二区三区| 亚洲一区二区久久久久久久| 91久久久久久久久久久| 91麻豆国产语对白在线观看| 日韩成人性视频| 欧美性猛交xxxx富婆弯腰| 久久久天堂国产精品女人| 欧美怡红院视频一区二区三区| 久久精品最新地址| 国产精品色悠悠| 亚洲激情 国产| 97精品一区二区视频在线观看| 超碰精品一区二区三区乱码| 欧美激情三级免费| 国产精品亚洲第一区| 国产精品爽黄69天堂a| 亚洲专区中文字幕| 久久久女人电视剧免费播放下载| 国产精品自拍偷拍视频| 欧美激情久久久久| 亚洲成人激情图| 成人深夜直播免费观看| 欧美激情一区二区久久久| 亚洲成人精品av| 国产精品欧美久久久| 亚洲精品98久久久久久中文字幕| 欧美电影在线免费观看网站| 久久精品中文字幕免费mv| 人人做人人澡人人爽欧美| 欧美专区中文字幕| 亚洲精品日韩在线| 国产日韩精品综合网站| 欧美一区二区.| 亚洲天堂av电影| 美女精品久久久| 日韩中文字幕久久| 国产在线观看精品一区二区三区| 国产日本欧美一区二区三区在线| 欧美怡春院一区二区三区| 97在线看福利| 亚洲欧美日韩久久久久久| 国产欧美精品久久久| 欧美综合在线观看| 久久精品国产69国产精品亚洲| 奇米四色中文综合久久| 黄网动漫久久久| 国产精品欧美日韩一区二区| 亚洲精品福利资源站| 另类美女黄大片| 亚洲国产精品yw在线观看| 成人激情视频在线播放| 日韩一区二区久久久| 亚洲精品福利在线| 日韩一区视频在线| 欧美三级欧美成人高清www| 国产精品久久久av久久久| 亚洲欧美制服另类日韩| 国产精品美女www爽爽爽视频| 国产啪精品视频网站| 亚洲一区二区三区xxx视频| 亚洲福利视频专区| 一区二区三区黄色| 国产成人一区二区在线| 精品国产欧美一区二区五十路| 精品亚洲一区二区三区在线观看| 欧美丝袜一区二区三区| 日本一区二区三区在线播放| 91视频免费网站| 正在播放亚洲1区| 国内精品久久久久久久久| 欧美性猛交xxxx免费看漫画| 久久综合久久美利坚合众国| 色综合久综合久久综合久鬼88| 欧美视频专区一二在线观看| 久久久噜噜噜久久中文字免| 一本色道久久88亚洲综合88| 日韩av电影院| 精品久久香蕉国产线看观看亚洲| 成人黄色短视频在线观看| 97不卡在线视频| 色青青草原桃花久久综合| 中文字幕日韩精品在线| 欧美在线一级视频| 精品久久久久久久大神国产| 亚洲免费中文字幕| 欧美国产在线视频| 一本大道亚洲视频| 九九视频直播综合网| 国内自拍欧美激情| 久热爱精品视频线路一| 成人欧美一区二区三区在线| 国产精品永久免费| 国产美女被下药99| 国产成人极品视频| 色悠悠久久久久| 精品综合久久久久久97| 亚洲精品女av网站| 久久精品99无色码中文字幕| 欧美大码xxxx| 国产免费一区二区三区香蕉精| 丝袜美腿精品国产二区| 亚洲一区中文字幕| 亚洲一区二区三区乱码aⅴ蜜桃女| 欧美一级淫片丝袜脚交| 久久成人在线视频| 国产日韩欧美一二三区| 精品亚洲精品福利线在观看| 人人做人人澡人人爽欧美| 91免费人成网站在线观看18| 亚洲美女av在线| 高清欧美电影在线| 欧美电影在线观看网站| 超薄丝袜一区二区| 91爱爱小视频k| 992tv在线成人免费观看| 成人国产在线视频| 欧美高清视频在线播放| 日韩经典第一页| 日韩亚洲综合在线| 狠狠躁夜夜躁久久躁别揉| 精品久久久久久| 日韩精品视频在线观看免费| 97在线观看免费| 亚洲国产精品久久久久秋霞蜜臀|