iOS彈幕的原理分析與實(shí)現(xiàn)(續(xù)篇)

寫(xiě)在前面

之前寫(xiě)了篇《彈幕的原理分析與實(shí)現(xiàn)》 的文章含末,最近有些閱讀的朋友提出了些疑問(wèn)佣盒,大家比較關(guān)注的一個(gè)問(wèn)題就是如何響應(yīng)彈幕的點(diǎn)擊事件,今天寫(xiě)這個(gè)續(xù)篇盯仪,主要是帶著大家一起實(shí)現(xiàn)一下功能全景。

上來(lái)開(kāi)搞

有些朋友可能覺(jué)得這太簡(jiǎn)單了牵囤,直接在彈幕的view上加一個(gè)tap的手勢(shì)不就完事了奔浅?于是乎汹桦,寫(xiě)了如下代碼:

//BulletView.m 文件中
- (void)addTapGesture {
    UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapHandler:)];
    [self addGestureRecognizer:tap];    
}

- (void)tapHandler:(UITapGestureRecognizer *)gesture {
    if  (self.tapBlock) {
         self.tapBlock();
    }
}

運(yùn)行程序,尼瑪钥弯,不起作用啊脆霎,是我寫(xiě)錯(cuò)了嗎,再次檢查一遍鹦马,全宇宙最通俗簡(jiǎn)單的幾行代碼荸频,怎么可能寫(xiě)錯(cuò)旭从,Why场仲?渠缕??付鹿?

iOS開(kāi)發(fā)機(jī)制蚜迅,當(dāng)view在animation的過(guò)程中谁不,是不會(huì)響應(yīng)任何事件的刹帕,所以移動(dòng)中的彈幕對(duì)我們這招免疫。作為一名合格的程序猿蹋辅,我們絕對(duì)不能在困難面前低頭侦另,此路不通我們換條路就行了。

言歸正傳

既然我們不能在移動(dòng)中的彈幕view上加點(diǎn)擊事件弃锐,那么我們是不是可以在彈幕view的父容器view中加點(diǎn)擊事件呢殿托,然后判斷這個(gè)點(diǎn)擊的點(diǎn)是否落在了這個(gè)彈幕view的范圍內(nèi)霹菊,如果是,我們就認(rèn)為你觸發(fā)了這個(gè)彈幕view的點(diǎn)擊事件支竹。

這么做還有一個(gè)好處就是我可以根據(jù)需求任意調(diào)整父容器view的位置旋廷,例如,默認(rèn)彈幕是顯示在屏幕下方的礼搁,如果點(diǎn)擊輸入框喚起了鍵盤(pán)柳洋,那么此時(shí)彈幕的位置應(yīng)該現(xiàn)在的鍵盤(pán)上方,所以我們只需要改變父容器view的y坐標(biāo)就可以達(dá)到目的叹坦。 尼瑪,果然程序猿的腦子天生就是用來(lái)解決問(wèn)題的募书。

手勢(shì)點(diǎn)擊.png

第一步,如圖所示测蹲,我們?cè)贑ontroller的View上添加一個(gè)BulletBackgroundView莹捡,然后將彈幕view添加在BulletBackgroundView上。

- (void)addBulletView:(BulletView *)bulletView {
    bulletView.frame = CGRectMake(CGRectGetWidth(self.view.frame)+50, 20 + 34 * bulletView.trajectory, CGRectGetWidth(bulletView.bounds), CGRectGetHeight(bulletView.bounds));
    [self.bulletBgView addSubview:bulletView];//添加到bulletBgView上
    [bulletView startAnimation];
}

此時(shí)我們要的效果是當(dāng)點(diǎn)擊A點(diǎn)的時(shí)候扣甲,不會(huì)有彈幕的點(diǎn)擊事件觸發(fā)篮赢,點(diǎn)擊B點(diǎn)時(shí)會(huì)響應(yīng)事件。說(shuō)起來(lái)容易琉挖,那么接下來(lái)如何實(shí)現(xiàn)呢启泣?

第二步,接受tapgesture事件并進(jìn)行點(diǎn)擊位置的判斷示辈,首先我們先看一下CALayer中有這么兩個(gè)方法寥茫,都可以達(dá)到我們的目的,

//返回包含某一點(diǎn)的最上層的子layer
- (nullable CALayer *)hitTest:(CGPoint)p;
//返回layer的bounds內(nèi)是否包含某一點(diǎn)
- (BOOL)containsPoint:(CGPoint)p;

這里邊我們選擇第一種方式矾麻,通過(guò)hitTest返回包含某個(gè)點(diǎn)的最上層的子layer纱耻,感興趣的同學(xué)也可以嘗試一下第二種方式。

這個(gè)點(diǎn)p我們可以通過(guò)gesture對(duì)象傳進(jìn)來(lái)险耀,代碼如下:

- (void)tapHandler:(UITapGestureRecognizer *)gesture {
      CGPoint clickPoint =  [gesture locationInView:self];
      //遍歷backgroundview上的所有subviews弄喘,其實(shí)就是所有的移動(dòng)的彈幕view了
      for (UIView *v in [self subviews]) {
        if ([v isKindOfClass:[BulletView class]]) {
            //返回point的最上層的layer,其實(shí)就是判斷point落在這個(gè)彈幕view范圍內(nèi)了
            if ([v.layer.presentationLayer hitTest:point]) {
                //處理點(diǎn)擊事件
                break;
            }
        }
    }
}

這樣子甩牺,我們就完成了一個(gè)移動(dòng)彈幕view的點(diǎn)擊事件了蘑志。按照這種方式運(yùn)行后,我們發(fā)現(xiàn),事件是響應(yīng)了卖漫,但是因?yàn)槭莻€(gè)tapGesture事件费尽,并不能像button那樣給用一個(gè)hilighted的效果,如何是好呢羊始?旱幼?既然這樣,我們就給用戶一個(gè)體驗(yàn)(代碼好多時(shí)候就是用來(lái)騙人的~~~)突委,在下邊這個(gè)方法中柏卤,添加幾行代碼。

if ([v.layer.presentationLayer hitTest:point]) {
      //處理點(diǎn)擊效果
      v.backgroundColor = [UIColor blueColor];
      dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
          v.backgroundColor = [UIColor redColor];
      });
      //處理點(diǎn)擊事件
      break;
}

寫(xiě)到這里匀油,我們應(yīng)該舉杯慶祝了吧缘缚,終于可以響應(yīng)點(diǎn)擊事件了,然而敌蚜,這樣子真的結(jié)束了嗎桥滨?程序猿的世界就是這樣子,解決了一個(gè)問(wèn)題弛车,我們又發(fā)現(xiàn)了另一個(gè)問(wèn)題~~~~~~~

高興的太早了

實(shí)際項(xiàng)目當(dāng)中齐媒,我們必定會(huì)遇到這樣一種情況,就是在BulletBackgroundView的下一層可能還會(huì)有響應(yīng)的視圖在纷跛,比如:

滑動(dòng)屏幕.png

對(duì)于新聞?lì)惖腶pp喻括,彈幕會(huì)飄在新聞詳情頁(yè)面上,然而我們需要實(shí)現(xiàn)在彈幕空白區(qū)域贫奠,可以去響應(yīng)后邊view的其他操作唬血,比如點(diǎn)擊、滑動(dòng)等唤崭。但按照我們上邊實(shí)現(xiàn)的機(jī)制拷恨,我們將tapGesture加在了BulletBackgroundView上,意味著點(diǎn)擊事件就不會(huì)傳到下一層view上了浩姥,想好了開(kāi)頭挑随,卻沒(méi)有想到結(jié)局,淚奔~~~

還有像這種的勒叠,在彈幕下邊還有一個(gè)按鈕button兜挨,那么當(dāng)沒(méi)有彈幕飄過(guò)時(shí),需要響應(yīng)button事件眯分,當(dāng)彈幕飄過(guò)拌汇,彈幕view和按鈕重合時(shí),需要響應(yīng)彈幕的事件而屏蔽掉button的事件弊决,

帶有按鈕.png

針對(duì)以上問(wèn)題噪舀,我們要進(jìn)一步對(duì)程序進(jìn)行改進(jìn)魁淳。

完美方案

根據(jù)以上分析,我們要解決兩個(gè)問(wèn)題:

  1. 如何讓下層的view也能響應(yīng)事件与倡,這個(gè)很簡(jiǎn)單界逛,我們只要把tapGesture加到最下邊的view上就可以保證事件不會(huì)被BulletBackgroundView吃掉了。
  2. 在下層事件和彈幕view事件沖突時(shí)纺座,如何保證執(zhí)行的是彈幕點(diǎn)擊事件息拜,而不是下層view的。這里我們就需要用到view的一個(gè)重載方法了
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event;

如果return nil净响,事件將向下層傳遞少欺,如果return self,事件將被本身view攔截掉不會(huì)向下層傳遞馋贤。解決了上邊兩個(gè)問(wèn)題赞别,我們來(lái)重新整理一下我們的代碼(《iOS彈幕的原理分析與實(shí)現(xiàn)》中代碼):

第一步,在UIViewController的View上添加BulletBackgroundView配乓,并且將彈幕添加到這個(gè)BulletBackgroundView上仿滔。

//UIViewController.m
- (void)viewDidLoad {
      //……
      [self.view addSubview:bulletView];
      //……
}
- (void)addBulletView:(BulletView *)bulletView {
    bulletView.frame = CGRectMake(CGRectGetWidth(self.view.frame)+50, 20 + 34 * bulletView.trajectory, CGRectGetWidth(bulletView.bounds), CGRectGetHeight(bulletView.bounds));
    [self.bulletBgView addSubview:bulletView];
    [bulletView startAnimation];
}

第二步,給UIViewController的View添加TapGesture事件犹芹。

//UIViewController.m
- (void)viewDidLoad {
      //……
      [self.view addSubview:bulletView];
      //綁定tap事件
      [self addTapGesture];
      //……
}
- (void)addTapGesture {
    UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapHandler:)];
    tap.cancelsTouchesInView = NO;
    [self.view addGestureRecognizer:tap];
    
}

- (void)tapHandler:(UITapGestureRecognizer *)gesture {
    //將處理的邏輯放到BulletBackgroundView中實(shí)現(xiàn)
    [self.bulletBgView dealTapGesture:gesture block:^(BulletView *bulletView){
        NSLog(@"%@", bulletView.lbComment.text);
    }];
}

第三步堤撵,在BulletBackgroundView中處理點(diǎn)擊事件判斷邏輯。

//BulletBackgroundView.m

//如果在當(dāng)前View中判斷了point落在的彈幕view范圍內(nèi)羽莺,則事件不在向下傳遞
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
    if ([self findClickBulletView:point]) {
        return self;
    }
    return nil;
}

- (BulletView *)findClickBulletView:(CGPoint)point {
    BulletView *bulletView = nil;
    for (UIView *v in [self subviews]) {
        if ([v isKindOfClass:[BulletView class]]) {
            //返回point的最上層的layer,其實(shí)就是判斷point落在這個(gè)彈幕view范圍內(nèi)了
            if ([v.layer.presentationLayer hitTest:point]) {
                bulletView = (BulletView *)v;
                break;
            }
        }
    }
    
    return bulletView;
}

//處理TapGesture事件
- (void)dealTapGesture:(UITapGestureRecognizer *)gesture block:(void (^)(BulletView *bulletView))block {
    CGPoint clickPoint =  [gesture locationInView:self];
    
    BulletView *bulletView = [self findClickBulletView:clickPoint];
    //找到了點(diǎn)擊的彈幕view洞豁,處理點(diǎn)擊效果盐固,并將bulletView傳回Controller進(jìn)行處理
    if (bulletView) {
        bulletView.backgroundColor = [UIColor blueColor];
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            bulletView.backgroundColor = [UIColor redColor];
        });
        if (block) {
            block(bulletView);
        }
    }

}

查看完成代碼,下載地址丈挟。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末刁卜,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子曙咽,更是在濱河造成了極大的恐慌蛔趴,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,755評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件例朱,死亡現(xiàn)場(chǎng)離奇詭異孝情,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)洒嗤,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)箫荡,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人渔隶,你說(shuō)我怎么就攤上這事羔挡〗嗄危” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,138評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵绞灼,是天一觀的道長(zhǎng)利术。 經(jīng)常有香客問(wèn)我,道長(zhǎng)低矮,這世上最難降的妖魔是什么印叁? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,791評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮商佛,結(jié)果婚禮上喉钢,老公的妹妹穿的比我還像新娘。我一直安慰自己良姆,他們只是感情好肠虽,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著玛追,像睡著了一般税课。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上痊剖,一...
    開(kāi)封第一講書(shū)人閱讀 51,631評(píng)論 1 305
  • 那天韩玩,我揣著相機(jī)與錄音,去河邊找鬼陆馁。 笑死找颓,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的叮贩。 我是一名探鬼主播击狮,決...
    沈念sama閱讀 40,362評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼益老!你這毒婦竟也來(lái)了彪蓬?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,264評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤捺萌,失蹤者是張志新(化名)和其女友劉穎档冬,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體桃纯,經(jīng)...
    沈念sama閱讀 45,724評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡酷誓,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了态坦。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片呛牲。...
    茶點(diǎn)故事閱讀 40,040評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖驮配,靈堂內(nèi)的尸體忽然破棺而出娘扩,到底是詐尸還是另有隱情着茸,我是刑警寧澤,帶...
    沈念sama閱讀 35,742評(píng)論 5 346
  • 正文 年R本政府宣布琐旁,位于F島的核電站涮阔,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏灰殴。R本人自食惡果不足惜敬特,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望牺陶。 院中可真熱鬧伟阔,春花似錦、人聲如沸掰伸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,944評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)狮鸭。三九已至合搅,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間歧蕉,已是汗流浹背灾部。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,060評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留惯退,地道東北人赌髓。 一個(gè)月前我還...
    沈念sama閱讀 48,247評(píng)論 3 371
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像催跪,于是被迫代替她去往敵國(guó)和親春弥。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評(píng)論 2 355

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

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,162評(píng)論 25 707
  • 2017.02.22 可以練習(xí)叠荠,每當(dāng)這個(gè)時(shí)候,腦袋就犯困扫责,我這腦袋真是神奇呀榛鼎,一說(shuō)讓你做事情,你就犯困鳖孤,你可不要太...
    Carden閱讀 1,346評(píng)論 0 1
  • 版本記錄 前言 在我們做直播等視頻類(lèi)app的時(shí)候者娱,總是有顯示和發(fā)送彈幕的要求,彈幕可以方便用戶進(jìn)行溝通和互動(dòng)苏揣,增加...
    刀客傳奇閱讀 5,129評(píng)論 8 16
  • 思念如火黄鳍,荼靡西天,無(wú)緣無(wú)故的想起你平匈,知道我還是在懷念你框沟,懷念你的陪伴藏古! 一直以來(lái),不喜歡有人打擾自己忍燥,每天最開(kāi)心...
    梅園遺珠閱讀 230評(píng)論 0 1
  • 腦海中循環(huán)著傳播學(xué)老師的一句話拧晕,就是,當(dāng)角色發(fā)生了變化時(shí)梅垄,雙方的關(guān)系將會(huì)發(fā)生改變厂捞。只是,有時(shí)候我不知道怎么處理~ ...
    惠木子小姐閱讀 184評(píng)論 0 0