iOS CoreText 實(shí)現(xiàn)帖子點(diǎn)贊文本(事件響應(yīng))

目前社交類型的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];

};

}

效果圖如下:

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容