【iOS】气泡样式视图

前言

在一个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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,384评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,845评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,148评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,640评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,731评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,712评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,703评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,473评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,915评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,227评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,384评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,063评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,706评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,302评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,531评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,321评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,248评论 2 352

推荐阅读更多精彩内容