這篇文章的前置文章:LNDanmakuMaster
彈幕容器通常需要覆蓋在播放器視圖上面,容器需要響應(yīng)那些有彈幕區(qū)域的手勢视卢,透傳那些沒有彈幕區(qū)域的手勢;如果希望使用CALayer系列組件做動效就需要額外處理手勢惠毁,因?yàn)橥ǔALayer是不能響應(yīng)手勢,所以崎页,我將這些繁瑣的處理封裝成ContainerView進(jìn)行統(tǒng)一處理鞠绰。
處理后的ContainerView有如下特性:
- 使用統(tǒng)一的一個TapGesture代替為每條彈幕都添加一個TapGesture。
- 觸碰那些沒有彈幕UI的區(qū)域時飒焦,手勢會被透傳到底層蜈膨。
- 彈幕的presentView支持自定義手勢,沒有自定義手勢時牺荠,走ContainerView默認(rèn)的Tap手勢翁巍,事件通過代理傳出去。
- 彈幕的presentLayer只支持默認(rèn)的Tap手勢休雌,事件通過代理傳出去灶壶。
- 實(shí)現(xiàn)了TrackController的裝載、卸載方法杈曲。
hitTest函數(shù)是ContainerView處理手勢的核心代碼:
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
if (self.hidden) {
return [super hitTest:point withEvent:event];
}
for (CALayer *layer in [self.layer.sublayers reverseObjectEnumerator]) {
if ([layer hitTest:point]) {
if (layer.danmakuAttributes) {
return self;
} else {
return [super hitTest:point withEvent:event];
}
break;
}
}
return nil;
}
判斷流程如下:
- 如果自身被隱藏驰凛,不響應(yīng)任何手勢胸懈。
- 遍歷containerView.Layer上的所有子Layer
- 如果Layer上能找到彈幕信息,說明這個Layer是個獨(dú)立的Layer恰响,可以直接響應(yīng)彈幕點(diǎn)擊事件趣钱,所以直接返回self,用自己的tap手勢處理胚宦。
- 如果Layer上找不到彈幕信息首有,說明這個Layer是通過addSubView添加的Layer,彈幕信息綁定在它的View上枢劝;這樣直接走View層級的手勢判斷:如果彈幕自己的View有自定義手勢則響應(yīng)自定義手勢绞灼,沒有會響應(yīng)containerView的tap手勢。
- 如果沒有找到子View響應(yīng)手勢呈野,那ContainerView自身也不會響應(yīng),返回nil印叁,手勢透傳到下一層級被冒。
注:倒序遍歷,因?yàn)檎{(diào)用addSubview/addSubLayer在不刻意設(shè)置zPosition時轮蜕,后加入的View/Layer通常覆蓋在前面加入的View/Layer上昨悼,所以,倒序遍歷的結(jié)果會更符合用戶視覺上的認(rèn)知跃洛。
用戶觸發(fā)手勢后走didTapped函數(shù)率触,這個函數(shù)的主要工作目標(biāo)是找到Attributes并通過代理把這個事件傳出去:
- (void)didTapped:(UITapGestureRecognizer *)tap
{
CGPoint point = [tap locationInView:self];
CALayer *tappedLayer = nil;
UIView *tappedView = nil;
for (CALayer *layer in [self.layer.sublayers reverseObjectEnumerator]) {
if ([layer hitTest:point]) {
if (layer.danmakuAttributes) {
tappedLayer = layer;
} else {
if ([layer.delegate isKindOfClass:[UIView class]]) {
tappedView = (UIView *)layer.delegate;
}
}
break;
}
}
LNDanmakuAbstractAttributes *targetAttributes;
if (tappedLayer) {
targetAttributes = [tappedLayer danmakuAttributes];
} else if (tappedView) {
targetAttributes = [tappedView danmakuAttributes];
}
if (targetAttributes && self.delegate && [self.delegate respondsToSelector:@selector(danmakuContainerDidTappedAttributes:)]) {
[self.delegate danmakuContainerDidTappedAttributes:targetAttributes];
}
}
(這里應(yīng)該先判斷是否有代理,沒有代理可以直接return汇竭,免得做多余的計算葱蝗,之后改一下)
建議:除了彈幕視圖之外最好不要在containerView上加其他的視圖,加在containerView的父視圖上细燎。