弹幕容器和手势:LNDanmakuContainerView
2021-02-05 本文已影响0人
BangRaJun
这篇文章的前置文章:LNDanmakuMaster
弹幕容器通常需要覆盖在播放器视图上面,容器需要响应那些有弹幕区域的手势,透传那些没有弹幕区域的手势;如果希望使用CALayer系列组件做动效就需要额外处理手势,因为通常CALayer是不能响应手势,所以,我将这些繁琐的处理封装成ContainerView进行统一处理。
处理后的ContainerView有如下特性:
- 使用统一的一个TapGesture代替为每条弹幕都添加一个TapGesture。
- 触碰那些没有弹幕UI的区域时,手势会被透传到底层。
- 弹幕的presentView支持自定义手势,没有自定义手势时,走ContainerView默认的Tap手势,事件通过代理传出去。
- 弹幕的presentLayer只支持默认的Tap手势,事件通过代理传出去。
- 实现了TrackController的装载、卸载方法。
hitTest函数是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;
}
判断流程如下:
- 如果自身被隐藏,不响应任何手势。
- 遍历containerView.Layer上的所有子Layer
- 如果Layer上能找到弹幕信息,说明这个Layer是个独立的Layer,可以直接响应弹幕点击事件,所以直接返回self,用自己的tap手势处理。
- 如果Layer上找不到弹幕信息,说明这个Layer是通过addSubView添加的Layer,弹幕信息绑定在它的View上;这样直接走View层级的手势判断:如果弹幕自己的View有自定义手势则响应自定义手势,没有会响应containerView的tap手势。
- 如果没有找到子View响应手势,那ContainerView自身也不会响应,返回nil,手势透传到下一层级。
注:倒序遍历,因为调用addSubview/addSubLayer在不刻意设置zPosition时,后加入的View/Layer通常覆盖在前面加入的View/Layer上,所以,倒序遍历的结果会更符合用户视觉上的认知。
用户触发手势后走didTapped函数,这个函数的主要工作目标是找到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];
}
}
(这里应该先判断是否有代理,没有代理可以直接return,免得做多余的计算,之后改一下)
建议:除了弹幕视图之外最好不要在containerView上加其他的视图,加在containerView的父视图上。