目前社交類型的App也是層出不窮,無論是QQ的說說,還是微信的動(dòng)態(tài),微博的帖子。這種類型的App都會(huì)涉及到點(diǎn)贊文本的顯示。
以下將介紹如何實(shí)現(xiàn)類型的文本點(diǎn)擊事件響應(yīng):
1.創(chuàng)建一個(gè)項(xiàng)目,創(chuàng)建一個(gè)類名為MyLabel,繼承UILabel.
2.在MyLabel.h中實(shí)現(xiàn)以下代碼:
//
//? MyLabel.h
//? CoreText_6_12
//
//? Created by he xiulian on 16/6/12.
//? Copyright ? 2016年 hexiulian. All rights reserved.
//
#import
/**
*? 選中哪個(gè)人回調(diào)信息
*
*? @param arrIndex 昵稱在的數(shù)組下標(biāo)
*? @param msg? ? ? 昵稱
*/
typedefvoid(^CallBackZan)(intarrIndex,NSString*msg);
@interfaceMyLabel:UILabel
{
//富文本樣式
NSMutableAttributedString*content;
}
@property(nonatomic,strong)NSMutableArray*mArrData;//點(diǎn)贊的信息數(shù)組
//回調(diào)屬性(賦值時(shí)實(shí)現(xiàn)代碼塊點(diǎn)擊某個(gè)昵稱時(shí)間響應(yīng)功能)
@property(nonatomic,strong)CallBackZanzanHandelBlock;
@end
3.在MyLabel.m中實(shí)現(xiàn)標(biāo)簽的初始化方法,設(shè)置文本樣式方法,實(shí)現(xiàn)點(diǎn)擊方法
3.1 在MyLabel.m中包含? #import
3.2 添加延展
//延展
@interfaceMyLabel()
{
CTFrameRef_frame;//ct的frame,coreText繪制要用
}
@end
3.3 實(shí)現(xiàn)初始化方法:
-(instancetype)initWithFrame:(CGRect)frame
{
if(self=[superinitWithFrame:frame])
{
self.mArrData=[NSMutableArraynew];
self.numberOfLines=0;//自動(dòng)換行
}
returnself;
}
3.4 重寫數(shù)組的set方法給文本賦值,并設(shè)置樣式:
//重寫數(shù)組的set方法
-(void)setMArrData:(NSMutableArray*)mArrData
{
//給數(shù)組賦值,注意不要寫self.mArrData
_mArrData=mArrData;
NSMutableString*strResult=[NSMutableStringstringWithString:@""];
//拼接文本
for(inti=0;i
{
[strResult appendString:@"@"];//拼接名字前的@
[strResult appendString:mArrData[i]];//拼接名字
if(i!=mArrData.count-1)//除了最后一個(gè)都加分割符
{
[strResult appendString:@"、"];
}
}
[strResult appendString:@"覺得很贊"];
//給文本賦值
self.text=strResult;
[selfbuildAttribute];
}
//創(chuàng)建NSMutableAttributedString,解析所有觸發(fā)點(diǎn)擊事件
-(void)buildAttribute
{
//獲取標(biāo)簽上的所有內(nèi)容轉(zhuǎn)為可修飾的富文本類型
content=[[NSMutableAttributedStringalloc]initWithString:self.text];
//這里對(duì)需要進(jìn)行點(diǎn)擊事件的字符heightlight效果
//點(diǎn)贊人的所有名字都是綠色的
[content? setAttributes:@{NSForegroundColorAttributeName:
[UIColorcolorWithRed:0.165green:0.604blue:0.212alpha:1.000]}
range:NSMakeRange(0,self.text.length-4)];
//倒數(shù)4個(gè)字符是灰色的
[content? setAttributes:@{NSForegroundColorAttributeName:
[UIColorcolorWithWhite:0.663alpha:1.000]}
range:NSMakeRange(self.text.length-4,4)];
//賦上新樣式的內(nèi)容
self.attributedText=content;
}
3.5 關(guān)于CoreText的一些知識(shí)補(bǔ)充:
1.boundingbox(邊界框bbox),這是一個(gè)假想的框子,它盡可能緊密的裝入字形。
2.baseline(基線),一條假想的線,一行上的字形都以此線作為上下位置的參考,在這條線的左側(cè)存在一個(gè)點(diǎn)叫做
基線的原點(diǎn),
3.ascent(上行高度)從原點(diǎn)到字體中最高(這里的高深都是以基線為參照線的)的字形的頂部的距離,ascent是
一個(gè)正值
4.descent(下行高度)從原點(diǎn)到字體中最深的字形底部的距離,descent是一個(gè)負(fù)值(比如一個(gè)字體原點(diǎn)到最深的
字形的底部的距離為2,那么descent就為-2)
5.linegap(行距),linegap也可以稱作leading(其實(shí)準(zhǔn)確點(diǎn)講應(yīng)該叫做Externalleading),行高
lineHeight則可以通過ascent+|descent|+linegap來計(jì)算。
Core Text繪制的流程:
CTFrame作為一個(gè)整體的畫布(Canvas),其中由行(CTLine)組成,而每行可以分為一個(gè)或多個(gè)小方塊(CTRun).
1.framesetterframesetter對(duì)應(yīng)的類型是CTFramesetter,通過CFAttributedString進(jìn)行初始化,
它作為CTFrame對(duì)象的生產(chǎn)工廠,負(fù)責(zé)根據(jù)path生產(chǎn)對(duì)應(yīng)的CTFrame
2.CTFrameCTFrame是可以通過CTFrameDraw函數(shù)直接繪制到context上的,可以在繪制之前,
操作CTFrame中的CTLine,進(jìn)行一些參數(shù)的微調(diào)
3。CTLine可以看做CoreText繪制中的一行的對(duì)象通過它可以獲得當(dāng)前行的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的繪制及實(shí)現(xiàn)響應(yīng):
-(void)drawRect:(CGRect)rect
{
//獲取上下文
CGContextRefcontext=UIGraphicsGetCurrentContext();
//設(shè)置context的ctm,用于適應(yīng)core text的坐標(biāo)體系
//保存現(xiàn)在得上下文圖形狀態(tài)。不管后續(xù)對(duì)context上繪制什么都不會(huì)影響真正得屏幕。
CGContextSaveGState(context);
//設(shè)置文本矩陣(為基矩陣)
CGContextSetTextMatrix(context,CGAffineTransformIdentity);
//x,y軸方向移動(dòng)
CGContextTranslateCTM(context,0,rect.size.height);
/*
Core Text一開始便是定位于桌面的排版系統(tǒng),使用了傳統(tǒng)的原點(diǎn)在左下角的坐標(biāo)系,所以它在繪制文本的時(shí)候都是參照左下角的原點(diǎn)進(jìn)行繪制的。 但是iOS的UIView的drawRect方法的context被做了次flip,如果你啥也不做處理,直接在這個(gè)context上進(jìn)行Core Text繪制,你會(huì)發(fā)現(xiàn)文字是鏡像且上下顛倒。
*/
//Core Graphics的context使用的坐標(biāo)系的原點(diǎn)是在左下角
//View為了其實(shí)現(xiàn)的便捷將原點(diǎn)變換到左上角
//所以在UIView的drawRect方法中的context上進(jìn)行Core Text繪制之前需要對(duì)context進(jìn)行一次Flip。
////縮放x,y軸方向縮放,-1.0為反向1.0倍,坐標(biāo)系轉(zhuǎn)換,沿x軸翻轉(zhuǎn)180度
CGContextScaleCTM(context,1.0,-1.0);
//設(shè)置CTFramesetter(獲取富文本的frame大小)
CTFramesetterRefframesetter=CTFramesetterCreateWithAttributedString((CFAttributedStringRef)content);
//創(chuàng)建可變的路徑
CGMutablePathRefpath=CGPathCreateMutable();
//添加路徑并設(shè)置frame
CGPathAddRect(path,NULL,CGRectMake(0,0,rect.size.width,rect.size.height));
//創(chuàng)建CTFrame
_frame=CTFramesetterCreateFrame(framesetter,CFRangeMake(0,content.length),path,NULL);
//把文字內(nèi)容繪制出來
CTFrameDraw(_frame,context);
//獲取畫出來的內(nèi)容的行數(shù)
CFArrayReflines=CTFrameGetLines(_frame);
//獲取每行的原點(diǎn)坐標(biāo)
CGPointlineOrigins[CFArrayGetCount(lines)];
//獲取行基線起點(diǎn)
CTFrameGetLineOrigins(_frame,CFRangeMake(0,0),lineOrigins);
//遍歷文本的行數(shù)
for(inti=0;i
{
CTLineRefline=CFArrayGetValueAtIndex(lines,i);
CGFloatlineAscent;//上行線
CGFloatlineDescent;//下行線
CGFloatlineLeading;//返回的主要線路
//獲取每行的寬度和高度
CTLineGetTypographicBounds(line,&lineAscent,&lineDescent,&lineLeading);
//獲取每個(gè)CTRun
CFArrayRefruns=CTLineGetGlyphRuns(line);
//遍歷每一行的每一個(gè)字符
for(intj=0;j
{
CGFloatrunAscent;
CGFloatrunDescent;
CGPointlineOrigin=lineOrigins[i];
//獲取每個(gè)CTRun
CTRunRefrun=CFArrayGetValueAtIndex(runs,j);
CGRectrunRect;
//調(diào)整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對(duì)象
UITouch*touch=[touches anyObject];
//獲取觸摸點(diǎn)擊當(dāng)前view的坐標(biāo)位置
CGPointlocation=[touch locationInView:self];
//獲取每一行
CFArrayReflines=CTFrameGetLines(_frame);
CGPointorigins[CFArrayGetCount(lines)];
//獲取每行的原點(diǎn)坐標(biāo)
CTFrameGetLineOrigins(_frame,CFRangeMake(0,0),origins);
CTLineRefline=NULL;
CGPointlineOrigin=CGPointZero;
for(inti=0;i
{
CGPointorigin=origins[i];
CGPathRefpath=CTFrameGetPath(_frame);
//獲取整個(gè)CTFrame的大小
CGRectrect=CGPathGetBoundingBox(path);
//坐標(biāo)轉(zhuǎn)換,把每行的原點(diǎn)坐標(biāo)轉(zhuǎn)換為uiview的坐標(biāo)體系(上下對(duì)稱)
CGFloaty=rect.origin.y+rect.size.height-origin.y;
//判斷點(diǎn)擊的位置處于哪一行范圍內(nèi)
if((location.y<=y)&&(location.x>=origin.x))
{
line=CFArrayGetValueAtIndex(lines,i);
lineOrigin=origin;
break;
}
}
location.x-=lineOrigin.x;
//獲取點(diǎn)擊位置所處的字符位置,就是相當(dāng)于點(diǎn)擊了第幾個(gè)字符
CFIndexindex=CTLineGetStringIndexForPosition(line,location);
//點(diǎn)最后四個(gè)字沒反應(yīng)
if(index>=self.text.length-4)
{
return;
}
//判斷點(diǎn)擊的字符是否在需要處理點(diǎn)擊事件的字符串范圍內(nèi),這里是hard code了需要觸發(fā)事件的字符串范圍
if(index>=1&&index<=self.text.length-4)
{
//存放選到的名字
NSString*strSelect=@"";
//記錄選中哪個(gè)下標(biāo)
longtextIndex=0;
for(inti=0;i
{
//逐個(gè)排除
strSelect=self.mArrData[i];
//下標(biāo)累加
textIndex+=strSelect.length;
//是選中的那個(gè)字符串
if(index<=textIndex)
{
self.zanHandelBlock(i,strSelect);
//找到
break;
}
textIndex++;//補(bǔ)充一個(gè)(@)的長(zhǎng)度
textIndex++;//補(bǔ)充一個(gè)(、)的長(zhǎng)度
}
}
}
4. 在ViewController.m中包含MyLabel.h創(chuàng)建標(biāo)簽:
#import "MyLabel.h"
-(void)viewDidLoad{
[superviewDidLoad];
//創(chuàng)建標(biāo)簽
MyLabel*lblSupport=[[MyLabelalloc]initWithFrame:CGRectMake(20,60,self.view.frame.size.width-40,120)];
//創(chuàng)建模擬數(shù)據(jù)
NSMutableArray*mArr=[NSMutableArrayarrayWithArray:@[@"似夢(mèng)非夢(mèng)",@"莊周夢(mèng)蝶",@"微光",@"依然懷舊",@"YouniTsa",@"一念執(zhí)著",@"不說話",@"BuLaiEng"]];
//給數(shù)組賦值,并給文本賦值
lblSupport.mArrData=mArr;
//打開用戶交互,響應(yīng)點(diǎn)擊事件
lblSupport.userInteractionEnabled=YES;
//顯示
[self.view addSubview:lblSupport];
//給點(diǎn)擊某個(gè)昵稱響應(yīng)事件的代碼塊賦值
lblSupport.zanHandelBlock=^(intarrIndex,NSString*nickName)
{
//控制器
UIAlertController*alertVC=[UIAlertControlleralertControllerWithTitle:@"提示"message:nickName preferredStyle:UIAlertControllerStyleAlert];
//確認(rèn)
UIAlertAction*ok=[UIAlertActionactionWithTitle:@"ok"style:UIAlertActionStyleDefaulthandler:^(UIAlertAction*_Nonnullaction){
}];
//取消
UIAlertAction*cancle=[UIAlertActionactionWithTitle:@"cancle"style:UIAlertActionStyleCancelhandler:^(UIAlertAction*_Nonnullaction){
}];
//添加響應(yīng)按鈕
[alertVC addAction:ok];
[alertVC addAction:cancle];
//彈框
[selfpresentViewController:alertVC animated:YES completion:nil];
};
}
效果圖如下: