美文网首页iOS旅程
iOS CoreText 实现帖子点赞文本(事件响应)

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

作者: 墨香茉香 | 来源:发表于2017-10-30 10:50 被阅读0次

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

    };

    }

    效果图如下:

    相关文章

      网友评论

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

        本文链接:https://www.haomeiwen.com/subject/bolppxtx.html