iOS彈幕開(kāi)發(fā)中遇到的問(wèn)題匯總
前言
彈幕在現(xiàn)在的各類(lèi)視頻中都有,也是每位開(kāi)發(fā)者們必須會(huì)的一個(gè)功能,最近在開(kāi)發(fā)中就遇到了一些問(wèn)題,下面簡(jiǎn)單說(shuō)說(shuō)彈幕開(kāi)發(fā)碰到的兩個(gè)小問(wèn)題。
正文
- 需求:實(shí)現(xiàn)一個(gè)彈幕容器,里面同時(shí)會(huì)有多行互不重疊的、運(yùn)動(dòng)中的彈幕 。每一條彈幕均需要支持點(diǎn)擊事件。
- 用腳底板想的方法:在彈幕容器里面創(chuàng)建幾個(gè) UIButton,并且 addTarget,增加點(diǎn)擊事件。最后利用 UIView 的 block API 實(shí)現(xiàn)動(dòng)畫(huà)。
- 結(jié)果:嗯...可惜的是,代碼運(yùn)行起來(lái),你會(huì)發(fā)現(xiàn)在 UIButton 運(yùn)動(dòng)過(guò)程,點(diǎn)擊事件并沒(méi)有響應(yīng),而且非常奇怪的是:為什么在 UIButton 動(dòng)畫(huà)過(guò)程,去點(diǎn)擊 UIButton 動(dòng)畫(huà)的終點(diǎn),點(diǎn)擊事件竟然響應(yīng)了??這是為什么呢?
- Core Anmation 動(dòng)畫(huà)過(guò)程原理的引用:
在iOS中,屏幕每秒鐘重繪60次。如果動(dòng)畫(huà)時(shí)長(zhǎng)比60分之一秒要長(zhǎng),Core Animation就需要在設(shè)置一次新值和新值生效之間,對(duì)屏幕上的圖層進(jìn)行重新組織。這意味著CALayer除了“真實(shí)”值(就是你設(shè)置的值)之外,必須要知道當(dāng)前顯示在屏幕上的屬性值的記錄。
每個(gè)圖層屬性的顯示值都被存儲(chǔ)在一個(gè)叫做呈現(xiàn)圖層的獨(dú)立圖層當(dāng)中,他可以通過(guò)-presentationLayer方法來(lái)訪問(wèn)。這個(gè)呈現(xiàn)圖層實(shí)際上是模型圖層的復(fù)制,但是它的屬性值代表了在任何指定時(shí)刻當(dāng)前外觀效果。換句話說(shuō),你可以通過(guò)呈現(xiàn)圖層的值來(lái)獲取當(dāng)前屏幕上真正顯示出來(lái)的值。
補(bǔ)充:模型圖層在動(dòng)畫(huà)開(kāi)始的那一刻就已經(jīng)達(dá)到終點(diǎn)位置,響應(yīng)點(diǎn)擊事件的也是它。
解決辦法:
重寫(xiě)彈幕容器 view 的 touchesBegan 方法。代碼如下:
@interface ZYYBarrageView ()
@property (nonatomic, strong) UIView *redView; // 將要做平移的 subview
@end
@implementation ZYYBarrageView
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
[self commonInit];
}
return self;
}
- (void)commonInit {
self.redView = [[UIView alloc] initWithFrame:CGRectMake(0.f, 0.f, 30.f, 30.f)];
self.redView.backgroundColor = [UIColor redColor];
[self addSubview:self.redView];
}
- (void)touchesBegan:(NSSet<uitouch *> *)touches withEvent:(UIEvent *)event {
// 重點(diǎn)開(kāi)始??!UITouch 獲取在 barrageView 坐標(biāo)系下的坐標(biāo)
CGPoint touchPoint = [[touches anyObject] locationInView:self];
// 判斷觸摸點(diǎn)是否在 redView 的呈現(xiàn)樹(shù)的框框之中
if ([self.redView.layer.presentationLayer hitTest:touchPoint]) {
// 響應(yīng)紅色塊點(diǎn)擊
return;
} else {
}
}</uitouch *>
進(jìn)一步的需求:在 ZYYBarrageView 的同一層級(jí),但層次偏后會(huì)有 UIButton。正常情況下,因?yàn)?ZYYBarrageView 的存在,UIButton 是無(wú)法響應(yīng)點(diǎn)擊事件的。代碼如下:
@property (nonatomic, strong) ZYYBarrageView *barrageView; // 彈幕 view 支持多行 view 在里面進(jìn)行運(yùn)動(dòng)
@property (nonatomic, strong) UIButton *yellowBtn; // 靠后的 UIButton
- (void)viewDidLoad {
[super viewDidLoad];
// self.yellowBtn 位于 self.barrageView 之后
[self.view addSubview:self.yellowBtn];
[self.view addSubview:self.barrageView];
}
- (ZYYBarrageView *)barrageView {
if (!_barrageView) {
_barrageView = [[ZYYBarrageView alloc] initWithFrame:CGRectMake(0.f, 30.f, SCREEN_WIDTH, 30.f)];
_barrageView.backgroundColor = [UIColor clearColor];
}
return _barrageView;
}
- (UIButton *)yellowBtn {
if (!_yellowBtn) {
_yellowBtn = [UIButton buttonWithType:UIButtonTypeCustom];
_yellowBtn.frame = CGRectMake(90.f, 30.f, 80.f, 30.f);
_yellowBtn.backgroundColor = [UIColor yellowColor];
[_yellowBtn setTitle:@"黃色按鈕" forState:UIControlStateNormal];
[_yellowBtn setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
[_yellowBtn addTarget:self action:@selector(onYellowBtn:) forControlEvents:UIControlEventTouchUpInside];
}
return _yellowBtn;
}
- (void)onYellowBtn:(id)sender {
// 響應(yīng)黃色按鈕
}
怎么辦?
Responder Chain 原理講解:手指點(diǎn)擊屏幕,經(jīng)過(guò)系統(tǒng)響應(yīng)(之前過(guò)程省略不說(shuō),文末有參考鏈接),調(diào)用 UIApplication 的 sendEvent: 方法,將 UIEvent 傳給 UIWindow, 通過(guò)遞歸調(diào)用 UIView 層級(jí)的 hitTest(_:with:) ,結(jié)合 point(inside:with:) 找到 UIEvent 中每一個(gè)UITouch 所屬的 UIView(其實(shí)是想找到離觸摸事件點(diǎn)最近的那個(gè) UIView)。這個(gè)過(guò)程是從 UIView 層級(jí)的最頂層往最底層遞歸查詢。同一層級(jí)的 UIView,會(huì)優(yōu)先深度遍歷界面靠前的 UIView。找到最底層 UIView 后,沿著 Responder Chain 逐步向上傳遞(UIControl 子類(lèi)默認(rèn)會(huì)攔截傳遞)。
解決思路:重寫(xiě) ZYYBarrageView 的 hitTest(_:with:) 方法。代碼如下:
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
BOOL isPointInsideSubview = [self.redView.layer.presentationLayer hitTest:point];
if (isPointInsideSubview == NO) {
// 如果沒(méi)有點(diǎn)擊在移動(dòng)的 redView 上,返回 nil
// 系統(tǒng)會(huì)去遍歷位于 ZYYBarrageView 后面的 UIButton,UIButton 能得到響應(yīng)
return nil;
} else {
return [super hitTest:point withEvent:event];
}
}
如此,可以完美解決啦~
總結(jié)
以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問(wèn)大家可以留言交流,謝謝大家對(duì)腳本之家的支持。
相關(guān)文章
Unity iOS混合開(kāi)發(fā)界面切換思路解析
這篇文章主要介紹了Unity iOS混合開(kāi)發(fā)界面切換思路解析的相關(guān)資料,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2016-09-09
Android 在頁(yè)面中顯示打包日期的實(shí)現(xiàn)方法
下面小編就為大家分享一篇Android 在頁(yè)面中顯示打包日期的實(shí)現(xiàn)方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-01-01
ios uicollectionview實(shí)現(xiàn)橫向滾動(dòng)
這篇文章主要為大家詳細(xì)介紹了ios uicollectionview實(shí)現(xiàn)橫向滾動(dòng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-03-03
IOS json 解析遇到錯(cuò)誤問(wèn)題解決辦法
這篇文章主要介紹了iOS json 解析遇到error: Error Domain=NSCocoaErrorDomain Code=3840 "The operation couldn’t be completed.解決辦法,需要的朋友可以參考下2017-01-01

