iOS 動(dòng)畫系列二(彈幕制作)

彈幕制作

barrage.gif
一、需求分析:

1.首先計(jì)算在指定區(qū)域你需要幾行彈幕
2.對(duì)使用過的label進(jìn)行緩存
3.每行彈幕進(jìn)入屏幕多少展运、這一行就可以進(jìn)入下一條彈幕坎弯。
4.如果這一行彈幕滿了就從第二行彈幕開始.以此類推。
5.如果最后所有行都滿了則加快彈幕播放速度
7.對(duì)彈幕上的文字以及圖片點(diǎn)擊時(shí)手勢(shì)的識(shí)別和添加
6.此外如果彈幕中有圖片頭像這些需要提前緩存下載数焊、以及其它性能優(yōu)化當(dāng)然本案列不會(huì)寫這么多辛润。

二膨处、設(shè)計(jì)如下:

總體采用面向?qū)ο蟮姆绞竭M(jìn)行邏輯的劃分、案列使用三行彈幕以隊(duì)列的方式保存每條消息的進(jìn)入與銷毀频蛔、通過字典保存每一行的狀態(tài)方便查找空閑行灵迫、每條彈幕內(nèi)部使用路徑layer動(dòng)畫實(shí)現(xiàn)layer移動(dòng)秦叛、通過系統(tǒng)定時(shí)器CADisplayLink記錄位置變化和改變狀態(tài).

1晦溪、重要類如下:
BarrageLabel 用來顯示具體彈幕內(nèi)容以及執(zhí)行內(nèi)部的動(dòng)畫等
BarrageLine 記錄某一行是否處于空閑狀態(tài)
BarrageVC 控制業(yè)務(wù)邏輯
如需擴(kuò)展建議添加一些消息類以及消息管理類。
2挣跋、重要屬性如下

//負(fù)責(zé)消息進(jìn)入與取出
@property(nonatomic, strong) NSMutableArray <NSMutableAttributedString *>* textQueue;
//保存當(dāng)前行狀態(tài)
@property(nonatomic, strong) NSMutableDictionary <NSNumber *, BarrageLine*>*barrageLineDic;
//緩存所有已創(chuàng)建的的label
@property(nonatomic, strong) NSMutableArray <BarrageLabel *>* totalLabels;
//正在屏幕上顯示的label
@property(nonatomic, strong) NSMutableArray<BarrageLabel *>* currentUsingLabels;
//目前處于空閑中未被使用的label
@property(nonatomic, strong) NSMutableArray<BarrageLabel *>* currentIdleLabels;

三三圆、代碼講解

1、N條假數(shù)據(jù)消息避咆,富文本實(shí)現(xiàn)圖文混排舟肉。并開啟啟動(dòng)定時(shí)器。由于彈幕目前是下一條緊跟上一條查库、如果某段時(shí)間沒有消息但是突然推送過來了消息將無法啟動(dòng)下一條繼續(xù)進(jìn)入加了此定時(shí)器幾秒檢測(cè)一次路媚,當(dāng)然你也可以其它優(yōu)化方案。

-(void)loadData
{
    for (int i =0 ; i< 10; i++) {
        NSMutableAttributedString *text = [NSMutableAttributedString new];
        
        //添加圖片
        NSTextAttachment *attchment = [[NSTextAttachment alloc]init];
        UIImage *image = [UIImage imageNamed:BBDefaultHeder];
        attchment.image = image;
        // 設(shè)置圖片大小
        attchment.bounds = CGRectMake(0, 0, 20, 20);
        NSAttributedString *stringImage = [NSAttributedString attributedStringWithAttachment:attchment];
        [text appendAttributedString:stringImage];
//        [text insertAttributedString:attachment atIndex:2];
        
        NSString *contentString = @"hello i come frome china.";
      NSMutableAttributedString  *stingAttri = [[NSMutableAttributedString alloc]initWithString:contentString];
        //設(shè)置這一行向上移動(dòng)5
        [stingAttri addAttribute:NSBaselineOffsetAttributeName value:@(5) range:NSMakeRange(0, contentString.length)];
     
        [text appendAttributedString:stingAttri];
        
        //再添加一個(gè)圖片
        [text appendAttributedString:stringImage];
        
       //加入隊(duì)列
        [self.textQueue addObject:text];
    }
    //開啟彈幕添加定時(shí)器
    [self.barrageTimer setFireDate:[NSDate distantPast]];
    
}

2樊销、對(duì)屏幕上的消息存入進(jìn)入緩存池整慎、目前一般長度的消息親測(cè)8個(gè)label以內(nèi)脏款。消息越短需要緩存的將會(huì)多些.

//添加一條彈幕
-(void)addABarrageUI
{
    if (self.textQueue.count == 0) {
        //沒有足夠的文字了--不需要顯示
        NSLog(@"Barrage- not enough text.!");
        //停掉定時(shí)器
        [self.barrageTimer setFireDate:[NSDate distantPast]];
        return;
    }
    int idleLine = [self getIdelLine];
    if (idleLine == -1) {
        //沒有空閑的行 ----進(jìn)行等待
        NSLog(@"Barrage- no enough idle line.!");
        return;
    }
    NSLog(@"Barrage- will add a barrage.");
     NSAttributedString *text = [self.textQueue firstObject];
    BarrageLabel *label;
    //計(jì)算文字的寬
    CGSize size0 = [self calculationTextSize:text.string cgSize:CGSizeMake(CGFLOAT_MAX, 30) font:16.0];//206.28
    CGFloat width =size0.width+20*2;//20是圖片的寬
        if (self.currentIdleLabels.count >0) {
            //有空閑緩存的label直接拿來使用
            label = [self.currentIdleLabels firstObject];
            NSLog(@"Barrage- get a cache idle label. count=%lu",self.currentIdleLabels.count);
        }else{
            //沒有空閑緩存的label-重新創(chuàng)建
                label = [[BarrageLabel alloc]initWithFrame:CGRectMake(-width, 0, width, 30)];
                [self.view addSubview:label];
                [self.totalLabels addObject:label];
        }
    label.frame = CGRectMake(-width, 0, width, 30);//更新尺寸寬
    label.attributedText = text;
//    label.backgroundColor = [UIColor greenColor];
    label.delegete = self;
    [self.textQueue removeObjectAtIndex:0];
    //開始動(dòng)畫
    [label startAnimationAtLine:idleLine];
}

3.代理實(shí)現(xiàn)label狀態(tài)回調(diào)記錄空閑行狀態(tài)變化、以及記錄可用label變化

-(void)visibleDidChange:(BarrageLabel *)label
{
//    NSLog(@"visibleDidChange");
    if (label.currentVisibleType == BarrageLabelVisibleTypeTailInScreen) {
        //可以添加標(biāo)記多了一個(gè)空閑位置
        BarrageLine *line = [self.barrageLineDic objectForKey:@(label.currentLine)];
        line.currentStatus = BarrageLineStatusIdle;
        if (self.isRuning) {
            //添加一個(gè)新的彈幕進(jìn)來了
            [self addABarrageUI];
        }
        
    }
}

-(void)stauesDidChange:(BarrageLabel *)label
{
//    NSLog(@"stauesDidChange");
    if (label.currentStatus != BarrageLabelStatusUsing) {
        //此label處于空閑狀態(tài)
        if ([self.currentUsingLabels containsObject:label]) {
            [self.currentUsingLabels removeObject:label];
        }
        if (self.currentIdleLabels.count > 20) {
            //開始清除多余的緩存label
        }
    }else{
        //此label正在被使用
        if (![self.currentUsingLabels containsObject:label]) {
            [self.currentUsingLabels addObject:label];
            NSLog(@"Barrage- current using label count is =%lu",self.currentUsingLabels.count);
            
        }
    }
   
}

4.移動(dòng)動(dòng)畫實(shí)現(xiàn)

 UIBezierPath *path = [self creatPathPoints:array];
    //創(chuàng)建動(dòng)畫
 CAAnimation *animation = [self creatAnimationPath:path];
  //開始動(dòng)畫
  [self.layer addAnimation:animation forKey:@"LLAnimationPosition"];
    
-(UIBezierPath *)creatPathPoints:(NSArray <NSValue *>*)pointValues
{
    UIBezierPath* path = [UIBezierPath bezierPath];
    path.lineWidth = 1.0;
    path.lineCapStyle = kCGLineCapRound; //線條拐角
    path.lineJoinStyle = kCGLineJoinRound; //終點(diǎn)處理
    for (int i = 0; i<pointValues.count; i++) {
        if (i==0) {
            //起點(diǎn)
            [path moveToPoint:pointValues[i].CGPointValue];
        }else{
            //連線
            [path addLineToPoint:pointValues[i].CGPointValue];
        }
    }
    return path;
}

-(CAKeyframeAnimation *)creatAnimationPath:(UIBezierPath *)path
{
    //添加動(dòng)畫
    CAKeyframeAnimation * animation;
    animation=[CAKeyframeAnimation animationWithKeyPath:@"position"];
    animation.path = path.CGPath;
    animation.duration = 8.0;
    animation.repeatCount=0;
    // 結(jié)束保持最后狀態(tài)
    //    animation.fillMode = kCAFillModeForwards;
    //線性
    [animation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]];
    [animation setDelegate:self];
    //動(dòng)畫執(zhí)行完不移除和fillmode都要設(shè)置
    //    [animation setRemovedOnCompletion:NO];
    return animation;
}

源碼
有疑問的小伙伴歡迎加交流討論QQ:206931384

喜歡的小伙伴們?cè)谖襣it上給個(gè)star感激不盡裤园、目前正在持續(xù)更新中喜歡關(guān)注的和建議小伙伴可以fork一下撤师。你的贊美是我努力的源泉我會(huì)加油的!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末拧揽,一起剝皮案震驚了整個(gè)濱河市剃盾,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌淤袜,老刑警劉巖痒谴,帶你破解...
    沈念sama閱讀 212,454評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異饮怯,居然都是意外死亡闰歪,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門蓖墅,熙熙樓的掌柜王于貴愁眉苦臉地迎上來库倘,“玉大人,你說我怎么就攤上這事论矾〗挑妫” “怎么了?”我有些...
    開封第一講書人閱讀 157,921評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵贪壳,是天一觀的道長饱亿。 經(jīng)常有香客問我,道長闰靴,這世上最難降的妖魔是什么彪笼? 我笑而不...
    開封第一講書人閱讀 56,648評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮蚂且,結(jié)果婚禮上配猫,老公的妹妹穿的比我還像新娘。我一直安慰自己杏死,他們只是感情好泵肄,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,770評(píng)論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著淑翼,像睡著了一般腐巢。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上玄括,一...
    開封第一講書人閱讀 49,950評(píng)論 1 291
  • 那天冯丙,我揣著相機(jī)與錄音,去河邊找鬼遭京。 笑死胃惜,一個(gè)胖子當(dāng)著我的面吹牛风宁,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播蛹疯,決...
    沈念sama閱讀 39,090評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼戒财,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了捺弦?” 一聲冷哼從身側(cè)響起饮寞,我...
    開封第一講書人閱讀 37,817評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎列吼,沒想到半個(gè)月后幽崩,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,275評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡寞钥,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,592評(píng)論 2 327
  • 正文 我和宋清朗相戀三年慌申,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片理郑。...
    茶點(diǎn)故事閱讀 38,724評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡蹄溉,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出您炉,到底是詐尸還是另有隱情柒爵,我是刑警寧澤,帶...
    沈念sama閱讀 34,409評(píng)論 4 333
  • 正文 年R本政府宣布赚爵,位于F島的核電站棉胀,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏冀膝。R本人自食惡果不足惜唁奢,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,052評(píng)論 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望窝剖。 院中可真熱鬧麻掸,春花似錦、人聲如沸枯芬。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,815評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽千所。三九已至,卻和暖如春蒜埋,著一層夾襖步出監(jiān)牢的瞬間淫痰,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,043評(píng)論 1 266
  • 我被黑心中介騙來泰國打工整份, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留待错,地道東北人籽孙。 一個(gè)月前我還...
    沈念sama閱讀 46,503評(píng)論 2 361
  • 正文 我出身青樓,卻偏偏與公主長得像火俄,于是被迫代替她去往敵國和親犯建。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,627評(píng)論 2 350

推薦閱讀更多精彩內(nèi)容

  • 1瓜客、通過CocoaPods安裝項(xiàng)目名稱項(xiàng)目信息 AFNetworking網(wǎng)絡(luò)請(qǐng)求組件 FMDB本地?cái)?shù)據(jù)庫組件 SD...
    陽明先生_X自主閱讀 15,969評(píng)論 3 119
  • 隨著時(shí)代的發(fā)展适瓦,女漢子也逐漸脫穎而出,現(xiàn)在很多女孩子谱仪,不像以前那樣柔柔弱弱又溫柔需要?jiǎng)e人保護(hù)了玻熙,堅(jiān)強(qiáng),獨(dú)立疯攒,可以自...
    鳩子歌閱讀 730評(píng)論 0 3
  • 看了這三頁不停的笑嗦随,清教徒的情節(jié)和無法閑下來。敬尺。枚尼。大部分人確實(shí)是這樣啊砂吞!即便給了假期仍然無法放松的狀況姑原,可以改變了。
    蘇醒love閱讀 361評(píng)論 0 0