基于Core Text实现的TXT电子书阅读器
本篇文章的项目地址基于Core Text实现的TXT电子书阅读器。
最近花了一点时间学习了iOS
的底层文字处理的框架Core Text
。在网上也参考很多资料,具体的资料在文章最后列了出来,有兴趣的可参考一下。
本篇主要介绍实现TXT
电子书阅读器设计用到的Core Text
相关的用法与实现。
关于Core Text
Core Text
是iOS
底层的文字处理框架,只提供一套C
函数接口,使用Core Text
对象时要注意手动管理内存以避免发生内存泄漏。之前写了一篇iOS富文本(二)初识Text Kit是介绍iOS
的另一个文字处理框架Text Kit
,Text Kit
是封装Core Text
函数提供一套Objective-C
的接口,使用起来也比较友好。高度的封装意味着可定制性差,灵活性低。所以如果需要实现更多的功能最好还是用Core Text
。
关于文字的相关知识参考文章最后列出的资料,因为涉及到字体大小的计算。例如在算字体高度时应该是baseline+ascent+descent的总和,了解这些知识对理解Core Text
相关函数很有帮助
Core Text运行时的层次
介绍一下这个层级。framesetter对象(CTFramesetterRef)最为顶层接收一个属性化字符串(attributedString)作为输入,一个framesetter对象生成一个或多个文本中的帧(CTFrameRef)每一个CTFrame都代表一个段落。
要生成帧(CTFrameRef)时,framesetter调用一个typesetter对象(CTTypesetterRef),它放置文本在frame中,framesetter设置段落样式给typesetter对象,包括属性对齐方式,制表位,行间距,缩进和换行模式,typesetter对象用这些属性转换每个字符成字形,然后在每行中填充这些字型,再用这些行填满整个绘制区间。
每个CTFrame对象包含段落线(CTLine)对象。每个(CTLine)对象代表段落中的每一行,一个CTFrame可以包含一个或者多个CTLine对象。CTLine由typesetter对象操作期间被创建。
每个CTLine是包含字形管理(CTRun)对象的数组,一个CTRun对象是一组共享相同属性,方向的连续字形。
基于Core Text实现的电子书阅读器
根据配置文件得到文字显示的属性。
+(NSDictionary *)parserAttribute:(LSYReadConfig *)config
{NSMutableDictionary *dict = [NSMutableDictionary dictionary];dict[NSForegroundColorAttributeName] = config.fontColor;dict[NSFontAttributeName] = [UIFont systemFontOfSize:config.fontSize];NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];paragraphStyle.lineSpacing = config.lineSpace;paragraphStyle.alignment = NSTextAlignmentJustified;dict[NSParagraphStyleAttributeName] = paragraphStyle;return [dict copy];
}
根据属性生成属性化字符串,然后属性化字符串作为输入得到CTFrame
对象
+(CTFrameRef)parserContent:(NSString *)content config:(LSYReadConfig *)parser bouds:(CGRect)bounds
{NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:content];NSDictionary *attribute = [self parserAttribute:parser];[attributedString setAttributes:attribute range:NSMakeRange(0, content.length)];CTFramesetterRef setterRef = CTFramesetterCreateWithAttributedString((__bridge CFAttributedStringRef)attributedString);CGPathRef pathRef = CGPathCreateWithRect(bounds, NULL);CTFrameRef frameRef = CTFramesetterCreateFrame(setterRef, CFRangeMake(0, 0), pathRef, NULL);CFRelease(setterRef);CFRelease(pathRef);return frameRef;}
生成的CTFrame在要View的drawRect
方法中调用CTFrameDraw
就可以进行绘制。
因为我们不仅要绘制出文字还要和文字进行交互所以仅仅这两个函数是不够的。
还需要以下函数
//根据触摸点获取当前文字的索引
+(CFIndex)parserIndexWithPoint:(CGPoint)point frameRef:(CTFrameRef)frameRef
{CFIndex index = -1;CGPathRef pathRef = CTFrameGetPath(frameRef); //获取绘制的路径CGRect bounds = CGPathGetBoundingBox(pathRef); //获取绘制的区间NSArray *lines = (__bridge NSArray *)CTFrameGetLines(frameRef); //获取绘制区间内所有的行数if (!lines) {return index;}NSInteger lineCount = [lines count];CGPoint *origins = malloc(lineCount * sizeof(CGPoint)); //给每行的起始点开辟内存if (lineCount) {CTFrameGetLineOrigins(frameRef, CFRangeMake(0, 0), origins); //获取每行的坐标for (int i = 0; i<lineCount; i++) {CGPoint baselineOrigin = origins[i];CTLineRef line = (__bridge CTLineRef)[lines objectAtIndex:i];CGFloat ascent,descent,linegap; //声明字体的上行高度和下行高度和行距CGFloat lineWidth = CTLineGetTypographicBounds(line, &ascent, &descent, &linegap); //获取每行的宽度CGRect lineFrame = CGRectMake(baselineOrigin.x, CGRectGetHeight(bounds)-baselineOrigin.y-ascent, lineWidth, ascent+descent+linegap+[LSYReadConfig shareInstance].lineSpace); //没有转换坐标系左下角为坐标原点 字体高度为上行高度加下行高度if (CGRectContainsPoint(lineFrame,point)){index = CTLineGetStringIndexForPosition(line, point); //得到当前文字的索引break;}}}free(origins); 释放内存return index;}
长按文字会默认选中两个文字这样就要计算选中的区间
+(CGRect)parserRectWithPoint:(CGPoint)point frameRef:(CTFrameRef)frameRef
{CFIndex index = -1;CGPathRef pathRef = CTFrameGetPath(frameRef);CGRect bounds = CGPathGetBoundingBox(pathRef);CGRect rect = CGRectZero;NSArray *lines = (__bridge NSArray *)CTFrameGetLines(frameRef);if (!lines) {return rect;}NSInteger lineCount = [lines count];CGPoint *origins = malloc(lineCount * sizeof(CGPoint)); //给每行的起始点开辟内存if (lineCount) {CTFrameGetLineOrigins(frameRef, CFRangeMake(0, 0), origins);for (int i = 0; i<lineCount; i++) {CGPoint baselineOrigin = origins[i];CTLineRef line = (__bridge CTLineRef)[lines objectAtIndex:i];CGFloat ascent,descent,linegap; //声明字体的上行高度和下行高度和行距CGFloat lineWidth = CTLineGetTypographicBounds(line, &ascent, &descent, &linegap);CGRect lineFrame = CGRectMake(baselineOrigin.x, CGRectGetHeight(bounds)-baselineOrigin.y-ascent, lineWidth, ascent+descent+linegap+[LSYReadConfig shareInstance].lineSpace); //没有转换坐标系左下角为坐标原点 字体高度为上行高度加下行高度加行间距 注:[LSYReadConfig shareInstance].lineSpace为配置文件中设置的行间距if (CGRectContainsPoint(lineFrame,point)){CFRange stringRange = CTLineGetStringRange(line);index = CTLineGetStringIndexForPosition(line, point);CGFloat xStart = CTLineGetOffsetForStringIndex(line, index, NULL); //获取当前索引在当前行的偏移量CGFloat xEnd;//默认选中两个单位if (index > stringRange.location+stringRange.length-2) {xEnd = xStart;xStart = CTLineGetOffsetForStringIndex(line,index-2,NULL);}else{xEnd = CTLineGetOffsetForStringIndex(line,index+2,NULL);}rect = CGRectMake(origins[i].x+xStart,baselineOrigin.y-descent,fabs(xStart-xEnd), ascent+descent);break;}}}free(origins);return rect;
}
上面就是实现这个项目使用的大部分关于Core Text
代码,实际项目实现起来远比这要复杂的多。具体实现请参考这个项目基于Core Text实现的TXT电子书阅读器
参考资料
Core Text 入门
基于 CoreText 的排版引擎:基础
Core Text Tutorial for iOS: Making a Magazine App
NIAttributedLabel.m
WFCoretext
基于Core Text实现的TXT电子书阅读器相关推荐
- C#实现电脑桌面端的本地txt电子书阅读器
写在前面的话 手机阅读是为了利用更多的碎片时间,但有时候桌面端阅读更方便,有需求就去找大牛的作品,试了几个,不是自己想要的,那就动手自己写一个了,满足基本功能的同时,也是一种自我学习和提高,分享出来给 ...
- 微信小程序|基于小程序+C#制作一个电子书阅读器
文章目录 一.文章前言 二.开发流程 2.1.开发工具 2.2.页面实现 2.3.数据库设计 2.4.API实现 一.文章前言 书籍是人类进步的阶梯,各位小伙伴在使用市面上各类阅读器进行阅读的时候是否 ...
- 安卓txt电子书阅读器源码
http://www.apkbus.com/forum.php?mod=viewthread&tid=64865
- 基于Android的本地电子书阅读器的设计与实现Ebook(1)
基于Android的本地电子书阅读器的设计与实现Ebook(1) 学习Android时间不久,试着做了一个本地电子书阅读器APP,因为知识浅薄并不能像其他大佬一样实现各种繁杂的功能,但可以实现基本的阅 ...
- Android电子书阅读器小程序(txt)
Android电子书阅读器小程序(txt) 开发环境 JDK 1.8 操作系统 Windows×32位或64位 可行性分析 技术可行性: 本项目应用的均是上课所学习的内容. 软件可行性: 用平时学习的 ...
- Delphi 10.4.2 轻松实现Android/IOS txt小说电子书阅读器应用APP翻页效果
Delphi 10.4.2是最新版本的跨平台本机应用开发工具,一套代码可编译到五个操作系统上:iOS.Android.Windows.macOS 和 Linux: 本代码仅仅数十行即可轻松实现Andr ...
- 有哪些能支持epub、txt格式的电子书阅读器?能在安卓手机上用的?
在手机上看书有许多方便之处,随着智能手机的硬件功能越来越发达,无论大学生还是工作族每天与手机端网络资源相接触已经成为了我们生活的常态.可是不得不说手机端打开资源的方式又常常会令我们头痛,那么如何能够又 ...
- 基于android的电子书阅读器app
基于android的电子书阅读器app 基于Android平台的电子书阅读器的设计与实现主要通过Eclipse开发工具, Java语言与Sqlite数据库来完成的.本阅读器实现了本地阅读,手动翻页,书 ...
- 基于安卓的电子书阅读器
功能描述: 1:用户在使用软件前需要先对软件进行注册,注册完成后通过账号和密码登录成功后,才可以对软件进行使用 2:用户登录成功后可以查看最新书籍信息,以及数据的分类,排行等信息,点击对应的分类可以查 ...
最新文章
- threadx 信号量 应用_ThreadX——IPC应用之事件标志
- 安装ubuntu时将boot目录单独挂载的意义
- Service Team在索引表CRMD_ORDER_INDEX中的存储设计
- 启迪公交上云助力北京公交二维码乘车业务系统顺利上线
- linux下ORACLE之RAW创建
- 根据wsdl文件生成WebService客户端代码
- 13、几点小结,unsigned long long
- Opencv笔记(二十一)——傅里叶变换
- Roslyn 入门:使用 .NET Core 版本的 Roslyn 编译并执行跨平台的静态的源码
- 如何识别POS机是一清机还是二清机?
- P1571 眼红的Medusa 题解
- 邮箱注册哪个好?哪家的邮箱最好用呀
- 计算机任务管理器设置软件启动,电脑软件开机自动启动,教你一招禁止启动,能够有效提升开机速度...
- TextView中加横线
- 学习Python的心得体会——阜阳师范大学 21级大数据管理与应用1班的同学不要抄哦
- 做一个简单网页(做一个简单网页多少钱)
- Java给定字符串形式的非负数,返回两个非负数的乘积
- IAR 使用中遇到的问题
- WDNet—2020ECCV
- 3DMax提示缺少Vrey的DLL
热门文章
- UVA11134 Fabled Rooks
- dellg3计算机rom,戴尔G3 U盘装系统win7教程
- 模拟贷款,设计贷款类Loan,Loan类包括贷款年利率(annualInterestRate),贷款年限(numberOfYears)、贷款额(loanAmount)......
- NFT - 2022年科技圈新宠
- 11091 最优自然数分解问题
- 使用Python的PyPD创建PDF文档
- 解决everything只能搜索C盘的问题
- 第九届河南省ACM省赛 D 导弹发射
- android u盘怎么打开文件夹图标不显示不出来了,如何解决U盘图标不显示但资源管理器中还能看到U盘...
- 一个barcode 多个 sku号_亚马逊SKU是什么?有什么作用?