弹幕容器和手势:LNDanmakuContainerView

这篇文章的前置文章: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的父视图上。

你可能感兴趣的:(弹幕容器和手势:LNDanmakuContainerView)