【iOS】气泡样式视图

作者: iHandle | 来源:发表于2018-10-11 21:31 被阅读869次

    前言

    在一个UITextField 或者 UITextView中长按,屏幕就会出现一个辅助编辑的工具条。这个工具条的轮廓是一个带三角形箭头的圆角矩形,看起来就像是一个气泡样式的提示框,这是一种非常常见的形状效果。
    UIKit里的UIPopoverPresentationController可以让一个Controller以这种形式展示。但是如果要对View进行操作,似乎就没有比较直接的方法,有一种解决方法是对其layermask属性进行设置。

    方法一: 用图片mask

    要做出这种效果,有一种速成方法。首先做一张下面这样的png图片,然后把这张图片设置为一个CALayer对象的contents,再用这个CALayermask目标Viewlayer

    代码示例:

    UIImage *img = [UIImage imageNamed:@"Bubble.png"];
    CALayer *maskLayer = [CALayer layer];
    maskLayer.frame = myView.bounds;
    maskLayer.contents = (__bridge id _Nullable)(img.CGImage);
    myView.layer.mask = maskLayer;     
    
    myView.layer.backgroundColor = [UIColor purpleColor].CGColor;
    
    

    很显然,这种实现方法可定制性不高,如果需要把三角形箭头的位置居中,或者把它放在矩形框的左方,又或者需要修改一下圆角的弧度......面对这些要求,可能需要做更多的png图片。

    方法二: 用“笔”画出来

    仔细看一下这个形状,它无非就是几条直线加上一些圆角,事实上完全可以用“笔”把它画出来。

    前期准备

    在开始绘制图形之前,我们需要先定位一些关键的“点”。如下图所示,灰色虚线外框是需要mask的目标视图的大小,绿色区域是最终要绘制的图形,而红色点的坐标在绘制过程中需要使用。


    假定我们按照顺时针方向绘制这个图形,并且先画“箭头”,再圆角矩形框。那么我们先把这些红点按照顺时针方向编号,并且暂时把它们放进数组里points中。

    除此之外,我们还要理解void CGContextAddArcToPoint(CGContextRef c, CGFloat x1, CGFloat y1, CGFloat x2, CGFloat y2, CGFloat radius)这个函数。前四个参数用来指定两个跟绘制有关的坐标点,最后一个参数用来指定圆角的半径(如果这个参数为0,则不会有圆角效果)。除了这五个参数以外,这个函数还会受一个坐标点影响,就是绘画的那支“笔”当前所在的坐标。
    举个例子,先看下面这张图:


    pointtApointB指定了函数的前四个参数,radius就是最后一个参数,“画笔”当前所在的坐标点就是Current point,那么上述函数根据这些参数绘制出来的曲线就是黑色的实线。
    动笔

    有了上面这些准备之后,终于可以动笔了。

    // 获取上下文
    UIGraphicsBeginImageContextWithOptions(self.size, NO, 0.0);
    CGContextRef ctx = UIGraphicsGetCurrentContext();
        
        
    // 因为第一步是要画箭头处的那个“0”的地方
    // 所以把“笔”放在“0”在顺时针方向顺序的上一个点: “6”
    CGPoint currentPoint = [[points objectAtIndex:6] CGPointValue];
    CGContextMoveToPoint(ctx, currentPoint.x, currentPoint.y);
        
    CGPoint pointA, pointB;
    CGFloat radius;
        
    int i = 0;
    while(i<7) {
        // 整个过程需要7次循环
                  
        // 箭头处(0,1,2三个点)是三个尖角,矩形框是四个圆角
        radius = i < 3 ?  0 : 10;
        // radius = i < 3 ? 4 : 10; // 全画成圆角
        pointA = [[points objectAtIndex:i] CGPointValue];
        // 画矩形框最后一个圆角的时候,pointB就是points[0]
        pointB = [[points objectAtIndex:(i+1)%7] CGPointValue];
    
        CGContextAddArcToPoint(ctx, pointA.x, pointA.y, pointB.x, pointB.y, radius);
        i = i + 1;
    }
    
    // 获取path
    CGContextClosePath(ctx);
    CGPathRef path = CGContextCopyPath(ctx);
    UIGraphicsEndImageContext();
    
    // 生成layer    
    CAShapeLayer *maskLayer = [CAShapeLayer layer];
    maskLayer.path = path;
    
    // 设置目标view的layer的mask属性    
    myView.layer.mask = maskLayer;
    
    
    代码封装

    由于我觉得平时可能经常会用到这种形状效果,所以我把它封装起来了。这样每次需要做这种效果的时候,只需根据Viewsize去调用接口就可以了。

    示例:

        BubbleLayer *bbLayer = [[BubbleLayer alloc]initWithSize:myView.bounds.size];
        // 提供的一些自定义设置
        // bubbleLayer.arrowDirection = ArrowDirectionTop;
        // bubbleLayer.arrowHeight = 12;
        // bubbleLayer.arrowWidth = 18;
        // bubbleLayer.arrowPosition = 0.3;
        [myView.layer setMask:[bbLayer layer]];
    
    

    封装的工作就是提供一些个性化的参数设置,比如“箭头”的高度、宽度、方向以及相对位置,还有矩形框的圆角半径这些。进一步的,还有根据这些个性化的参数和Viewsize,去计算上面说到的7个关键点。这些工作都比较简单,所以我就不再赘述了,完整代码(包括Swift版)我放在了Github仓库。

    仓库链接: https://github.com/iHandle/BubbleLayer

    相关文章

      网友评论

        本文标题:【iOS】气泡样式视图

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