美文网首页iOS开发攻城狮的集散地程序员
梯形按钮的绘制与响应处理

梯形按钮的绘制与响应处理

作者: dj_rose | 来源:发表于2018-06-08 15:56 被阅读69次
    项目首页大改版,有个UI需求是这样的:
    首页.gif
    汽车票、火车票两个tab需要做成梯形的样式。并且切tab的时候,梯形的样式需要随之变化。
    这边的梯形绘制是比较简单的,直接用贝塞尔曲线绘制即可:
    - (void)refreshBusTabWithSel:(BOOL)bSel
    {
        if (_busBGLayer) {
            [_busBGLayer removeFromSuperlayer];
        }
        CGFloat differX = -HomeTabDifferX;
        UIColor *fillColor = RGBA(0, 0, 0, 0.3);
        UIColor *titleColor = def_text_White;
        if (bSel) {
            differX = HomeTabDifferX;
            fillColor = def_text_White;
            titleColor = def_BG_Blue;
        }
        UIBezierPath *pathBus = [UIBezierPath bezierPath];
        [pathBus moveToPoint:CGPointMake(0, 0)];
        [pathBus addLineToPoint:CGPointMake(HomeProjectTabWidth/2 - differX, 0)];
        [pathBus addLineToPoint:CGPointMake(HomeProjectTabWidth/2 + differX, HomeTabHeight)];
        [pathBus addLineToPoint:CGPointMake(0 , HomeTabHeight)];
        [pathBus closePath];
        [pathBus fill];
        
        _busBGLayer = [[CAShapeLayer alloc] init];
        _busBGLayer.frame = CGRectMake(0, 0, HomeProjectTabWidth/2 + HomeTabDifferX,HomeTabHeight);
        _busBGLayer.path = pathBus.CGPath;
        _busBGLayer.fillColor = fillColor.CGColor;
        [self.btnBusTab.layer insertSublayer:_busBGLayer atIndex:0];
        [self.btnBusTab setTitleColor:titleColor forState:UIControlStateNormal];
    }
    
    主要想记录一下的是对于点击区域的处理,如何在梯形变化的时候响应正确的点击事件~
    - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
    {
        [super hitTest:point withEvent:event];
        CGFloat minX = HomeProjectTabWidth/2-HomeTabDifferX;
        CGFloat maxX = HomeProjectTabWidth/2+HomeTabDifferX;
        CGFloat ratio = HomeTabHeight/(HomeTabDifferX*2);
        CGFloat pointY = HomeTabHeight - point.y;
        if ([self pointInside:point withEvent:event]) {
            if (point.x <= minX) {
                return _btnBusTab;
            }
            if (point.x >= maxX) {
                return _btnTrainTab;
            }
            if (_bSelTrainTab) {
                NSDebugLog(@"@@@%d",pointY > (point.x-minX)*ratio);
                return pointY > (point.x-minX)*ratio ? _btnBusTab : _btnTrainTab;
            } else {
                NSDebugLog(@"@@@%d",pointY > (point.x-minX)*(-ratio) + HomeTabHeight);
                return pointY > (point.x-minX)*(-ratio) + HomeTabHeight ? _btnTrainTab : _btnBusTab;
            }
        } else {
            return [super hitTest:point withEvent:event];
        }
    }
    
    hitTest:withEvent:

    每个UIView对象都有一个 hitTest:withEvent: 方法,这个方法是Hit-Testing过程中最核心的存在,其作用是询问事件在当前视图中的响应者,同时又是作为事件传递的桥梁。
    这边重写了两个tab的父View的hitTest方法,需要特殊处理的就是中间那条斜线所在的x坐标范围。

    如何处理斜线所在x区域的点击?

    利用一次函数(形如y=kx+b)的概念将斜线作为一次函数放置在数学平面坐标系中,通过计算点击的坐标是在函数的上方/下方,来判断点击区域是在汽车票tab还是火车票。

    y=kx.png
    值得注意一下的是,当点击坐标不在这两个tab范围内的时候,需要调用一下[super hitTest:point withEvent:event],以保证点击到坐标范围外可以响应其他本该响应的事件,而不是被我们重写的这个方法消耗掉。

    注:如果对hitTest等响应链相关知识不太熟悉的,可以看一下这篇:iOS触摸事件全家桶

    相关文章

      网友评论

        本文标题:梯形按钮的绘制与响应处理

        本文链接:https://www.haomeiwen.com/subject/rsmosftx.html