直接附代码:

#import "MyView.h"
#import <CoreText/CoreText.h>// 行距
const CGFloat kGlobalLineLeading = 5.0;// 在15字体下,比值小于这个计算出来的高度会导致emoji显示不全
const CGFloat kPerLineRatio = 1.4;@interface MyView()@property (nonatomic ,assign) CGFloat textHeight;@end@implementation MyView
- (instancetype)initWithFrame:(CGRect)frame{self = [super initWithFrame:frame];if (self) {self.text = @"我自横刀向天笑,去留肝胆两昆仑。--谭嗣同同学你好啊。This is my first CoreText demo,how are you ?I love three things,the sun,the moon,and you.the sun for the day,the moon for the night,and you forever.��������������去年今日此门中,人面桃花相映红。人面不知何处去,桃花依旧笑春风。��������������少年不知愁滋味,爱上层楼,爱上层楼,为赋新词强说愁。56321363464.而今识尽愁滋味,欲说还休,欲说还休,却道天凉好个秋。123456,7890,56321267895434。缺月挂疏桐,漏断人初静。谁见幽人独往来,缥缈孤鸿影。惊起却回头,有恨无人省。捡尽寒枝不肯栖,寂寞沙洲冷。";self.font = [UIFont systemFontOfSize:15];}return self;
}
/***  高度 = 每行的固定高度 * 行数*/
+ (CGFloat)textHeightWithText:(NSString *)aText width:(CGFloat)aWidth font:(UIFont *)aFont{NSMutableAttributedString *content = [[NSMutableAttributedString alloc] initWithString:aText];// 给字符串设置字体行距等样式[self addGlobalAttributeWithContent:content font:aFont];CTFramesetterRef framesetterRef = CTFramesetterCreateWithAttributedString((__bridge CFAttributedStringRef)content);// 粗略的高度,该高度不准,仅供参考CGSize suggestSize = CTFramesetterSuggestFrameSizeWithConstraints(framesetterRef, CFRangeMake(0, content.length), NULL, CGSizeMake(aWidth, MAXFLOAT), NULL);NSLog(@"suggestHeight = %f",suggestSize.height);CGMutablePathRef pathRef = CGPathCreateMutable();CGPathAddRect(pathRef, NULL, CGRectMake(0, 0, aWidth, suggestSize.height));CTFrameRef frameRef = CTFramesetterCreateFrame(framesetterRef, CFRangeMake(0, content.length), pathRef, NULL);CFArrayRef lines = CTFrameGetLines(frameRef);CFIndex lineCount = CFArrayGetCount(lines);NSLog(@"行数 = %ld",lineCount);// 总高度 = 行数*每行的高度,其中每行的高度为指定的值,不同字体大小不一样CGFloat accurateHeight = lineCount * (aFont.pointSize * kPerLineRatio);CGFloat height = accurateHeight;CFRelease(pathRef);CFRelease(frameRef);return height;
}
#pragma mark - 工具方法
#pragma mark 给字符串添加全局属性,比如行距,字体大小,默认颜色
+ (void)addGlobalAttributeWithContent:(NSMutableAttributedString *)aContent font:(UIFont *)aFont
{CGFloat lineLeading = kGlobalLineLeading; // 行间距const CFIndex kNumberOfSettings = 2;//设置段落格式CTParagraphStyleSetting lineBreakStyle;CTLineBreakMode lineBreakMode = kCTLineBreakByWordWrapping;lineBreakStyle.spec = kCTParagraphStyleSpecifierLineBreakMode;lineBreakStyle.valueSize = sizeof(CTLineBreakMode);lineBreakStyle.value = &lineBreakMode;//设置行距CTParagraphStyleSetting lineSpaceStyle;CTParagraphStyleSpecifier spec;spec = kCTParagraphStyleSpecifierLineSpacingAdjustment;lineSpaceStyle.spec = spec;lineSpaceStyle.valueSize = sizeof(CGFloat);lineSpaceStyle.value = &lineLeading;// 结构体数组CTParagraphStyleSetting theSettings[kNumberOfSettings] = {lineBreakStyle,lineSpaceStyle,};CTParagraphStyleRef theParagraphRef = CTParagraphStyleCreate(theSettings, kNumberOfSettings);// 将设置的行距应用于整段文字[aContent addAttribute:NSParagraphStyleAttributeName value:(__bridge id)(theParagraphRef) range:NSMakeRange(0, aContent.length)];CFStringRef fontName = (__bridge CFStringRef)aFont.fontName;CTFontRef fontRef = CTFontCreateWithName(fontName, aFont.pointSize, NULL);// 将字体大小应用于整段文字[aContent addAttribute:NSFontAttributeName value:(__bridge id)fontRef range:NSMakeRange(0, aContent.length)];// 给整段文字添加默认颜色[aContent addAttribute:NSForegroundColorAttributeName value:[UIColor blackColor] range:NSMakeRange(0, aContent.length)];// 内存管理CFRelease(theParagraphRef);CFRelease(fontRef);
}
#pragma mark - 一行一行绘制,行高确定,行与行之间对齐
#pragma mark - 一行一行绘制,行高确定,高度不够时加上省略号
- (void)drawRectWithLineByLineAlignmentAndEllipses{// 1.创建需要绘制的文字NSMutableAttributedString *attributed = [[NSMutableAttributedString alloc] initWithString:self.text];// 2.设置行距等样式[[self class] addGlobalAttributeWithContent:attributed font:self.font];self.textHeight = [[self class] textHeightWithText:self.text width:CGRectGetWidth(self.bounds) font:self.font];// 3.创建绘制区域,path的高度对绘制有直接影响,如果高度不够,则计算出来的CTLine的数量会少一行或者少多行CGMutablePathRef path = CGPathCreateMutable();CGPathAddRect(path, NULL, CGRectMake(0, 0, CGRectGetWidth(self.bounds), self.textHeight*2));// 4.根据NSAttributedString生成CTFramesetterRefCTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)attributed);CTFrameRef ctFrame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, attributed.length), path, NULL);// 重置高度CGFloat realHeight = self.textHeight;// 绘制全部文本需要的高度大于实际高度则调整,并加上省略号if (realHeight > CGRectGetHeight(self.frame)){realHeight = CGRectGetHeight(self.frame);}NSLog(@"realHeight = %f",realHeight);// 获取上下文CGContextRef contextRef = UIGraphicsGetCurrentContext();// 转换坐标系CGContextSetTextMatrix(contextRef, CGAffineTransformIdentity);CGContextTranslateCTM(contextRef, 0, realHeight); // 这里跟着调整CGContextScaleCTM(contextRef, 1.0, -1.0);// 这里可调整可不调整CGPathAddRect(path, NULL, CGRectMake(0, 0, CGRectGetWidth(self.bounds), realHeight));// 一行一行绘制CFArrayRef lines = CTFrameGetLines(ctFrame);CFIndex lineCount = CFArrayGetCount(lines);CGPoint lineOrigins[lineCount];// 把ctFrame里每一行的初始坐标写到数组里,注意CoreText的坐标是左下角为原点CTFrameGetLineOrigins(ctFrame, CFRangeMake(0, 0), lineOrigins);CGFloat frameY = 0;for (CFIndex i = 0; i < lineCount; i++){// 遍历每一行CTLineCTLineRef line = CFArrayGetValueAtIndex(lines, i);CGFloat lineAscent;CGFloat lineDescent;CGFloat lineLeading; // 行距// 该函数除了会设置好ascent,descent,leading之外,还会返回这行的宽度CTLineGetTypographicBounds(line, &lineAscent, &lineDescent, &lineLeading);//CoreText的origin的Y值是在baseLine处,而不是下方的descent。CGPoint lineOrigin = lineOrigins[i];//行高CGFloat lineHeight = self.font.pointSize * kPerLineRatio;//self.font.descender为负值frameY = realHeight - (i + 1)*lineHeight - self.font.descender;NSLog(@"frameY = %f",frameY);lineOrigin.y = frameY;//调整坐标CGContextSetTextPosition(contextRef, lineOrigin.x, lineOrigin.y);if (frameY + self.font.descender > lineHeight){CTLineDraw(line, contextRef);}else{NSLog(@"最后一行");// 最后一行,加上省略号static NSString* const kEllipsesCharacter = @"\u2026";CFRange lastLineRange = CTLineGetStringRange(line);// 一个emoji表情占用两个长度单位NSLog(@"range.location = %ld,range.length = %ld,总长度 = %ld",lastLineRange.location,lastLineRange.length,attributed.length);if (lastLineRange.location + lastLineRange.length < (CFIndex)attributed.length){// 这一行放不下所有的字符(下一行还有字符),则把此行后面的回车、空格符去掉后,再把最后一个字符替换成省略号CTLineTruncationType truncationType = kCTLineTruncationEnd;NSUInteger truncationAttributePosition = lastLineRange.location + lastLineRange.length - 1;// 拿到最后一个字符的属性字典NSDictionary *tokenAttributes = [attributed attributesAtIndex:truncationAttributePositioneffectiveRange:NULL];// 给省略号字符设置字体大小、颜色等属性NSAttributedString *tokenString = [[NSAttributedString alloc] initWithString:kEllipsesCharacterattributes:tokenAttributes];// 用省略号单独创建一个CTLine,下面在截断重新生成CTLine的时候会用到CTLineRef truncationToken = CTLineCreateWithAttributedString((__bridge CFAttributedStringRef)tokenString);// 把这一行的属性字符串复制一份,如果要把省略号放到中间或其他位置,只需指定复制的长度即可NSUInteger copyLength = lastLineRange.length;NSMutableAttributedString *truncationString = [[attributed attributedSubstringFromRange:NSMakeRange(lastLineRange.location, copyLength)] mutableCopy];if (lastLineRange.length > 0){// Remove any whitespace at the end of the line.unichar lastCharacter = [[truncationString string] characterAtIndex:copyLength - 1];// 如果复制字符串的最后一个字符是换行、空格符,则删掉if ([[NSCharacterSet whitespaceAndNewlineCharacterSet] characterIsMember:lastCharacter]){[truncationString deleteCharactersInRange:NSMakeRange(copyLength - 1, 1)];}}// 拼接省略号到复制字符串的最后[truncationString appendAttributedString:tokenString];// 把新的字符串创建成CTLineCTLineRef truncationLine = CTLineCreateWithAttributedString((__bridge CFAttributedStringRef)truncationString);// 创建一个截断的CTLine,该方法不能少,具体作用还有待研究CTLineRef truncatedLine = CTLineCreateTruncatedLine(truncationLine, self.frame.size.width, truncationType, truncationToken);if (!truncatedLine){// If the line is not as wide as the truncationToken, truncatedLine is NULLtruncatedLine = CFRetain(truncationToken);}CFRelease(truncationLine);CFRelease(truncationToken);CTLineDraw(truncatedLine, contextRef);CFRelease(truncatedLine);} else{// 这一行刚好是最后一行,且最后一行的字符可以完全绘制出来CTLineDraw(line, contextRef);}// 跳出循环,避免绘制剩下的多余的CTLinebreak;}}CFRelease(path);CFRelease(framesetter);CFRelease(ctFrame);
}- (void)drawRect:(CGRect)rect
{[self drawRectWithLineByLineAlignmentAndEllipses];
}
@end

调用:

- (void)viewDidLoad {[super viewDidLoad];self.view.backgroundColor = [UIColor whiteColor];// Do any additional setup after loading the view, typically from a nib.MyView *view = [[MyView alloc]initWithFrame:CGRectMake(0, 20, self.view.width, 100)];view.backgroundColor = [UIColor redColor];view.text = @"我自横刀向天笑,去留肝胆两昆仑。--谭嗣同同学你好啊。This is my first CoreText demo,how are you ?I love three things,the sun,the moon,and you.the sun for the day,the moon for the night,and you forever.��������������去年今日此门中,人面桃花相映红。人面不知何处去,桃花依旧笑春风。��������������少年不知愁滋味,爱上层楼,爱上层楼,为赋新词强说愁。56321363464.而今识尽愁滋味,欲说还休,欲说还休,却道天凉好个秋。123456,7890,56321267895434。缺月挂疏桐,漏断人初静。谁见幽人独往来,缥缈孤鸿影。惊起却回头,有恨无人省。捡尽寒枝不肯栖,寂寞沙洲冷。";view.font = [UIFont systemFontOfSize:15];[self.view addSubview:view];
}

结果:

参考:
CoreText使用教程(四)

CoreText(五):省略号相关推荐

  1. 五、当文字过多时,显示省略号

    overflow: hiddenwhite-space: nowraptext-overflow: ellipsis

  2. 一级计算机综合训练第五卷,2018职称计算机考试基础综合练习及答案(5)

    2018职称计算机考试基础综合练习及答案(5) 1.WIndom 2000是一个多用户多任务 2.发送到有以下四个(我的文档.3.5寸软盘.邮件接受者.桌面快捷方式) 3.当要选定多个连续的文件或文件 ...

  3. python numpy库中省略号...的一些用法

    在学习<Designing Machine Learning Systems with Python>(<机器学习系统设计Python语言实现>)一书的第五章梯度下降一节代码中 ...

  4. 使用CoreText实现图文混排

    2019独角兽企业重金招聘Python工程师标准>>> OS没有现成的支持图文混排的控件,而要用多个基础控件组合拼成图文混排这样复杂的排版,是件很苦逼的事情.对此的解决方案有使用Co ...

  5. 五、Web App 基础可视组件属性(IVX 快速开发教程)

    五.基础可视组件属性 在 iVX 中各个组件存在不同的属性,这些属性用于设置显示的样式或者是自身具备的特征等,通过更改这些属性可以极大的方便我们进行项目的创作. 大多数组件都拥有相同的属性,相同属性在 ...

  6. 使用 jQuery Mobile 与 HTML5 开发 Web App (五) —— jQuery Mobile 表单下

    在上文<使用 jQuery Mobile 与 HTML5 开发 Web App -- jQuery Mobile 表单上>中, Kayo 介绍了一部分 jQuery Mobile 表单组件 ...

  7. html分页 页码中间省略,jquery实现了一个分页,如何是现实固定的页码数,查过部分用页码省略号代替?...

    墨色风雨 好久之前,参考论坛分页后端模板输出逻辑弄过一个.        function makePage (total, page) {//生成页码            var pages = ...

  8. [转]Windows Shell编程 第十五章【来源:http://blog.csdn.net/wangqiulin123456/article/details/7988016】...

    第十五章 SHELL扩展 谈到Windows Shell编程,Shell扩展是最重要的科目之一,绝大多数商业应用的最酷特征的都是通过Shell扩展实现的,而且有许多显著的系统特征实际都是插入了扩展代码 ...

  9. uniapp 乘法结果保留了很多小数_苏教版五年级数学上册5.9循环小数和商的近似值微课视频 | 练习...

    第一单元.负数的初步认识 1.1<初步认识正负数>视频讲解 同步练习 1.2<负数在实际生活中的应用(1)> 1.3<负数在实际生活中的应用(2)> 五年级上册第一 ...

  10. matlab 省略号,matlab知识集锦 | 学步园

     matlab中的inv函数是什么意思 inv是求逆矩阵.x'是矩阵转置(对复数矩阵而言是共轭转置). matlab rgb2gray()函数是基于什么原理编写的,或者这个函数的代码? 以R.G. ...

最新文章

  1. Awcing算法--双指针
  2. 如何手撸一个较为完整的RPC框架
  3. jstl:sql标签介绍
  4. Android【FileInputStream、FileOutputStream---本地文件I/O操作-读写操作模板(btnWrite方法、btnRead方法)】
  5. 计算机考研:计算机网络五大考点解析
  6. Visual SourceSafe 数据库安全性简介
  7. Windows下显示目录大小及文件个数
  8. 【OS学习笔记】二十二 保护模式六:保户模式下操作系统内核如何加载用户程序并运行 对应的汇编代码之用户程序
  9. 想要高清壁纸,高图桌面壁纸网站值得收藏!
  10. js中while死循环语句_如何检测JavaScript中的死循环?
  11. 一文看懂:边缘计算究竟是什么?为何潜力无限?(下)
  12. 测试用例设计——场景法
  13. 香港房地产业发展历程、现状、问题及对策探究
  14. Redis_04_Redis客户端
  15. 转:接班人都是“剩出来”的,选接班人9条必用原则
  16. 线上营销和线下营销如何完美结合?
  17. 最全最强解析:支付宝钱包系统架构内部剖析(架构图)
  18. iOS客户端React-Native增量更新实践
  19. linux开放端口和关闭端口
  20. [Render] 适用于高级Unity创作者的通用渲染管线[4] - URP中的光照

热门文章

  1. 网站选用老域名还是新域名好?
  2. windows下使用HHKB键盘,配置autohotkey键位映射
  3. 一个能够快速下载Git.exe历史版本的方法
  4. os系统服务器防火墙怎么关闭,mac防火墙如何关闭
  5. 9张图揭秘:优秀的数据分析项目,这样做!
  6. 网络安全风险评估原理
  7. 12C Sharding 学习安装
  8. 运筹优化(十三)--大规模优化方法
  9. 统一接口平台(一) 产品介绍
  10. Android Studio 插件整理