目前社交类型的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];
};
}
效果图如下:
网友评论