iOS CoreText 实现帖子点赞文本(事件响应)

目前社交类型的App也是层出不穷,无论是QQ的说说,还是微信的动态,微博的帖子。这种类型的App都会涉及到点赞文本的显示。

以下将介绍如何实现类型的文本点击事件响应:

1.创建一个项目,创建一个类名为MyLabel,继承UILabel.

2.在MyLabel.h中实现以下代码:

//

//  MyLabel.h

//  CoreText_6_12

//

//  Created by he xiulian on 16/6/12.

//  Copyright ? 2016年 hexiulian. All rights reserved.

//

#import

/**

*  选中哪个人回调信息

*

*  @param arrIndex 昵称在的数组下标

*  @param msg      昵称

*/

typedefvoid(^CallBackZan)(intarrIndex,NSString*msg);

@interfaceMyLabel:UILabel

{

//富文本样式

NSMutableAttributedString*content;

}

@property(nonatomic,strong)NSMutableArray*mArrData;//点赞的信息数组

//回调属性(赋值时实现代码块点击某个昵称时间响应功能)

@property(nonatomic,strong)CallBackZanzanHandelBlock;

@end

3.在MyLabel.m中实现标签的初始化方法,设置文本样式方法,实现点击方法

3.1 在MyLabel.m中包含  #import

3.2 添加延展

//延展

@interfaceMyLabel()

{

CTFrameRef_frame;//ct的frame,coreText绘制要用

}

@end

3.3 实现初始化方法:

-(instancetype)initWithFrame:(CGRect)frame

{

if(self=[superinitWithFrame:frame])

{

self.mArrData=[NSMutableArraynew];

self.numberOfLines=0;//自动换行

}

returnself;

}

3.4 重写数组的set方法给文本赋值,并设置样式:

//重写数组的set方法

-(void)setMArrData:(NSMutableArray*)mArrData

{

//给数组赋值,注意不要写self.mArrData

_mArrData=mArrData;

NSMutableString*strResult=[NSMutableStringstringWithString:@""];

//拼接文本

for(inti=0;i

{

[strResult appendString:@"@"];//拼接名字前的@

[strResult appendString:mArrData[i]];//拼接名字

if(i!=mArrData.count-1)//除了最后一个都加分割符

{

[strResult appendString:@"、"];

}

}

[strResult appendString:@"觉得很赞"];

//给文本赋值

self.text=strResult;

[selfbuildAttribute];

}

//创建NSMutableAttributedString,解析所有触发点击事件

-(void)buildAttribute

{

//获取标签上的所有内容转为可修饰的富文本类型

content=[[NSMutableAttributedStringalloc]initWithString:self.text];

//这里对需要进行点击事件的字符heightlight效果

//点赞人的所有名字都是绿色的

[content  setAttributes:@{NSForegroundColorAttributeName:

[UIColorcolorWithRed:0.165green:0.604blue:0.212alpha:1.000]}

range:NSMakeRange(0,self.text.length-4)];

//倒数4个字符是灰色的

[content  setAttributes:@{NSForegroundColorAttributeName:

[UIColorcolorWithWhite:0.663alpha:1.000]}

range:NSMakeRange(self.text.length-4,4)];

//赋上新样式的内容

self.attributedText=content;

}

3.5 关于CoreText的一些知识补充:

1.boundingbox(边界框bbox),这是一个假想的框子,它尽可能紧密的装入字形。

2.baseline(基线),一条假想的线,一行上的字形都以此线作为上下位置的参考,在这条线的左侧存在一个点叫做

基线的原点,

3.ascent(上行高度)从原点到字体中最高(这里的高深都是以基线为参照线的)的字形的顶部的距离,ascent是

一个正值

4.descent(下行高度)从原点到字体中最深的字形底部的距离,descent是一个负值(比如一个字体原点到最深的

字形的底部的距离为2,那么descent就为-2)

5.linegap(行距),linegap也可以称作leading(其实准确点讲应该叫做Externalleading),行高

lineHeight则可以通过ascent+|descent|+linegap来计算。

Core Text绘制的流程:

CTFrame作为一个整体的画布(Canvas),其中由行(CTLine)组成,而每行可以分为一个或多个小方块(CTRun).

1.framesetterframesetter对应的类型是CTFramesetter,通过CFAttributedString进行初始化,

它作为CTFrame对象的生产工厂,负责根据path生产对应的CTFrame

2.CTFrameCTFrame是可以通过CTFrameDraw函数直接绘制到context上的,可以在绘制之前,

操作CTFrame中的CTLine,进行一些参数的微调

3。CTLine可以看做CoreText绘制中的一行的对象通过它可以获得当前行的line ascent,line descent

,line leading,还可以获得Line下的所有GlyphRuns

4。CTRun或者叫做GlyphRun,是一组共享想相同attributes(属性)的字形的集合体

可参考:http://my.oschina.net/megan/blog/269042

可参考:http://blog.csdn.net/fengsh998/article/details/8691823/

3.6  core Text的绘制及实现响应:

-(void)drawRect:(CGRect)rect

{

//获取上下文

CGContextRefcontext=UIGraphicsGetCurrentContext();

//设置context的ctm,用于适应core text的坐标体系

//保存现在得上下文图形状态。不管后续对context上绘制什么都不会影响真正得屏幕。

CGContextSaveGState(context);

//设置文本矩阵(为基矩阵)

CGContextSetTextMatrix(context,CGAffineTransformIdentity);

//x,y轴方向移动

CGContextTranslateCTM(context,0,rect.size.height);

/*

Core Text一开始便是定位于桌面的排版系统,使用了传统的原点在左下角的坐标系,所以它在绘制文本的时候都是参照左下角的原点进行绘制的。 但是iOS的UIView的drawRect方法的context被做了次flip,如果你啥也不做处理,直接在这个context上进行Core Text绘制,你会发现文字是镜像且上下颠倒。

*/

//Core Graphics的context使用的坐标系的原点是在左下角

//View为了其实现的便捷将原点变换到左上角

//所以在UIView的drawRect方法中的context上进行Core Text绘制之前需要对context进行一次Flip。

////缩放x,y轴方向缩放,-1.0为反向1.0倍,坐标系转换,沿x轴翻转180度

CGContextScaleCTM(context,1.0,-1.0);

//设置CTFramesetter(获取富文本的frame大小)

CTFramesetterRefframesetter=CTFramesetterCreateWithAttributedString((CFAttributedStringRef)content);

//创建可变的路径

CGMutablePathRefpath=CGPathCreateMutable();

//添加路径并设置frame

CGPathAddRect(path,NULL,CGRectMake(0,0,rect.size.width,rect.size.height));

//创建CTFrame

_frame=CTFramesetterCreateFrame(framesetter,CFRangeMake(0,content.length),path,NULL);

//把文字内容绘制出来

CTFrameDraw(_frame,context);

//获取画出来的内容的行数

CFArrayReflines=CTFrameGetLines(_frame);

//获取每行的原点坐标

CGPointlineOrigins[CFArrayGetCount(lines)];

//获取行基线起点

CTFrameGetLineOrigins(_frame,CFRangeMake(0,0),lineOrigins);

//遍历文本的行数

for(inti=0;i

{

CTLineRefline=CFArrayGetValueAtIndex(lines,i);

CGFloatlineAscent;//上行线

CGFloatlineDescent;//下行线

CGFloatlineLeading;//返回的主要线路

//获取每行的宽度和高度

CTLineGetTypographicBounds(line,&lineAscent,&lineDescent,&lineLeading);

//获取每个CTRun

CFArrayRefruns=CTLineGetGlyphRuns(line);

//遍历每一行的每一个字符

for(intj=0;j

{

CGFloatrunAscent;

CGFloatrunDescent;

CGPointlineOrigin=lineOrigins[i];

//获取每个CTRun

CTRunRefrun=CFArrayGetValueAtIndex(runs,j);

CGRectrunRect;

//调整CTRun的rect

runRect.size.width=CTRunGetTypographicBounds(run,CFRangeMake(0,0),&runAscent,&runDescent,NULL);

runRect=CGRectMake(lineOrigin.x+CTLineGetOffsetForStringIndex(line,CTRunGetStringRange(run).location,NULL),lineOrigin.y-runDescent,runRect.size.width,runAscent+runDescent);

}

}

//绘制

CGContextRestoreGState(context);

}

//接受触摸事件

-(void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event

{

//获取UITouch对象

UITouch*touch=[touches anyObject];

//获取触摸点击当前view的坐标位置

CGPointlocation=[touch locationInView:self];

//获取每一行

CFArrayReflines=CTFrameGetLines(_frame);

CGPointorigins[CFArrayGetCount(lines)];

//获取每行的原点坐标

CTFrameGetLineOrigins(_frame,CFRangeMake(0,0),origins);

CTLineRefline=NULL;

CGPointlineOrigin=CGPointZero;

for(inti=0;i

{

CGPointorigin=origins[i];

CGPathRefpath=CTFrameGetPath(_frame);

//获取整个CTFrame的大小

CGRectrect=CGPathGetBoundingBox(path);

//坐标转换,把每行的原点坐标转换为uiview的坐标体系(上下对称)

CGFloaty=rect.origin.y+rect.size.height-origin.y;

//判断点击的位置处于哪一行范围内

if((location.y<=y)&&(location.x>=origin.x))

{

line=CFArrayGetValueAtIndex(lines,i);

lineOrigin=origin;

break;

}

}

location.x-=lineOrigin.x;

//获取点击位置所处的字符位置,就是相当于点击了第几个字符

CFIndexindex=CTLineGetStringIndexForPosition(line,location);

//点最后四个字没反应

if(index>=self.text.length-4)

{

return;

}

//判断点击的字符是否在需要处理点击事件的字符串范围内,这里是hard code了需要触发事件的字符串范围

if(index>=1&&index<=self.text.length-4)

{

//存放选到的名字

NSString*strSelect=@"";

//记录选中哪个下标

longtextIndex=0;

for(inti=0;i

{

//逐个排除

strSelect=self.mArrData[i];

//下标累加

textIndex+=strSelect.length;

//是选中的那个字符串

if(index<=textIndex)

{

self.zanHandelBlock(i,strSelect);

//找到

break;

}

textIndex++;//补充一个(@)的长度

textIndex++;//补充一个(、)的长度

}

}

}

4. 在ViewController.m中包含MyLabel.h创建标签:

#import "MyLabel.h"

-(void)viewDidLoad{

[superviewDidLoad];

//创建标签

MyLabel*lblSupport=[[MyLabelalloc]initWithFrame:CGRectMake(20,60,self.view.frame.size.width-40,120)];

//创建模拟数据

NSMutableArray*mArr=[NSMutableArrayarrayWithArray:@[@"似梦非梦",@"庄周梦蝶",@"微光",@"依然怀旧",@"YouniTsa",@"一念执着",@"不说话",@"BuLaiEng"]];

//给数组赋值,并给文本赋值

lblSupport.mArrData=mArr;

//打开用户交互,响应点击事件

lblSupport.userInteractionEnabled=YES;

//显示

[self.view addSubview:lblSupport];

//给点击某个昵称响应事件的代码块赋值

lblSupport.zanHandelBlock=^(intarrIndex,NSString*nickName)

{

//控制器

UIAlertController*alertVC=[UIAlertControlleralertControllerWithTitle:@"提示"message:nickName preferredStyle:UIAlertControllerStyleAlert];

//确认

UIAlertAction*ok=[UIAlertActionactionWithTitle:@"ok"style:UIAlertActionStyleDefaulthandler:^(UIAlertAction*_Nonnullaction){

}];

//取消

UIAlertAction*cancle=[UIAlertActionactionWithTitle:@"cancle"style:UIAlertActionStyleCancelhandler:^(UIAlertAction*_Nonnullaction){

}];

//添加响应按钮

[alertVC addAction:ok];

[alertVC addAction:cancle];

//弹框

[selfpresentViewController:alertVC animated:YES completion:nil];

};

}

效果图如下:

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

推荐阅读更多精彩内容