iOS使用CoreText完成txt阅读器

 CoreText是一个高效处理字符和字形转换和进行文字排版的框架,API基于C语言。

常见的CoreText类介绍

(1)、CFAttributedStringRef
属性字符串,用于存储需要绘制的文字字符和字符属性

(2)、CTFramesetterRef
framesetter对应的类型是 CTFramesetter,通过CFAttributedStringRef进行初始化,它作为CTFrame对象的生产工厂,负责根据path生产对应的CTFrame;

(3)、CTFrame
CTFrame是可以通过CTFrameDraw函数直接绘制到context上的,当然你可以在绘制之前,操作CTFrame中的CTLine,进行一些参数的微调;

(3)、CTLine
在CTFrame内部是由多个CTLine来组成的,每个CTLine代表一行;可以看做Core Text绘制中的一行的对象 通过它可以获得当前行的line ascent,line descent ,line leading,还可以获得Line下的所有Glyph Runs;

(4)、CTRun
或者叫做 Glyph Run,每个CTLine又是由多个CTRun组成的,每个CTRun代表一组显示风格一致的文本,是一组共享想相同attributes(属性)的字形的集合体;

渲染流程

  1. 当我们需要排版时,可以对字符串设置各种格式,生成NSAttributeString;
  2. 然后用NSAttributeString去创建CTFramesetter类,
  3. CTFramesetter会处理排版信息,然后生成排版后的结果CTFrame;
  4. CTFrame是一段或者多段文本,每段文本又由多行文字组成,每行的表示为CTLine;
  5. CTLine是一行文本,每行文本由多个CTRun组成,CTRun是一小段连续的字形;
  6. CTTypeSetter负责上下文相关排版处理,比如说换行,每个CTFrame中都会有一个CTTypeSetter; 他们之间的关系图如下:

总的来说,CTFramesetter是生成CTFrame的工厂类,初始化参数是attributed string,会在内部创建CTTypesetter并进行实际的排版;

CTLine类似每一行的文字,CTRun是一行中具有相同属性的连续字形,比如说“我正在分享阅读器”,就会由三个CTRun组成,分别是“我正在”、“分享”、“阅读器”(因为“分享”两个字加粗了,否则就会是一个CTRun)。

 CoreText的使用流程:

  1. 使用core text就是有一个要显示的string,
  2. 然后定义这个string每个部分的样式生成富文本attributedString
  3. 由富文本生成 CTFramesetter
  4. CTFramesetter得到CTFrame
  5. 使用绘制(CTFrameDraw)CTFrame 

关键函数介绍

由富文本字符串得到CTFramesetter

  • CTFramesetterCreateWithAttributedString(att as CFAttributedString)
  • CFAttributedString是NSAttributedString的CF对象,可以直接强转;

CTFramesetterRef CTFramesetterCreateWithAttributedString( CFAttributedStringRef string );  

CTFramesetter包含了富文本字符串的布局信息和相关属性,供后续的绘制操作使用。最主要的作用就是生成下面的CTFrame。

通过调用 CTFramesetterCreateWithAttributedString 函数,可以将富文本字符串转换为 Core Text 的布局对象,为后续的绘制操作提供所需的文本排版和属性信息。这样,你就可以使用 Core Text 提供的更多功能来自定义文本的布局、字体、颜色等,并实现高度定制化的文本渲染效果。

CTFramesetterRef 对象并不直接进行绘制操作,它只包含了文本布局的信息。要将文本绘制到图形上下文中,还需要使用 CTFrameDraw 函数创建并绘制 CTFrameRef 对象。

生成CTFrame

  • CTFramesetterCreateFrame(framesetter, CFRangeMake(pageStart, 0), path, nil)

使用 CTFramesetterRef 对象、文本范围、路径和其他参数创建一个 CTFrameRef 对象,

CTFrame是排版数据,可直接通过重写View的drawRect方法渲染到页面上

  • framesetter:上面创建的CTFramesetterRef
  • stringRange:要使用的文本范围,即 CFRange 结构体。
    • 可以通过设置 CFRangeMake 参数来确定要使用的富文本字符串的起始位置和长度
    • 如果范围的长度部分设置为0,比如CFRangeMake(location, 0),则会尽可能的填满CTFrame,将继续添加行,直到文本或空间用完。
  • path:绘制文本的路径,即 CGPathRef 类型对象。
    • 路径定义了文本应该在画布上的布局方式和区域。
    • 一般传渲染View的bounds即可
  • frameAttributes:可选的附加属性字典,提供额外的布局控制和属性设置。

计算分页

  • CTFrameGetVisibleStringRange(frame)

CTFrameGetVisibleStringRange 函数的作用是获取给定文本框架(CTFrame)中可见的文本范围。可见的范围是指在当前文本框架大小和路径下实际可见的文本部分。

返回值: CTFrameGetVisibleStringRange 函数返回一个 CFRange 结构体,表示给定文本框架中可见的文本范围。该范围包括起始位置(location)和长度(length)信息。

比如原文有1W字,当前的frame只能显示200字,那么返回的Range就是(0,200),下一页在从200的基础上进行计算,比如第二页算出为(200,430),在下一页就从430开始计算,如此循环就可计算出这1W字需要分多少页,并且每页内容的CTFrame都已生成。

通过上面的介绍,把这几个函数连起来,就是数据准备阶段的核心方法:

  1. 根据txt内容生成String -> 在由String生成富文本-> 由富文本生成framesetter,
  2. 根据页面大小计算生成单页的CTFrame
  3. CTFrame获取当前Frame有效的文字显示范围,下一页的location累加,循环计算分页,保存得到每页的内容范围和每页的CTFrame
func createCTFrame(contentStr: String) {let range = NSMakeRange(0, contentStr.count)let att = NSMutableAttributedString(string: contentStr)att.addAttribute(.foregroundColor, value: UIColor.lightGray, range: range)att.addAttribute(.font, value: UIFont.systemFont(ofSize: 22), range: range)let framesetter = CTFramesetterCreateWithAttributedString(att as CFAttributedString)let path = CGPath(rect: self.readView.bounds, transform: nil)var pageStart = 0var frameArray: [CTFrame] = []var i: Int = 0repeat {let frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(pageStart, 0), path, nil)let pageRange = CTFrameGetVisibleStringRange(frame)let beginIndex = contentStr.index(contentStr.startIndex, offsetBy: pageRange.location)let endIndex = contentStr.index(beginIndex, offsetBy: pageRange.length)let onePage = String(contentStr[beginIndex..<endIndex])pageStart = pageRange.location + pageRange.lengthprint("第\(i)页" ,pageRange, onePage)i+=1frameArray.append(frame)} while(pageStart < contentStr.count )self.frameArray = frameArray}

渲染方法

  • CTFrameDraw(frame, ctx)

渲染的核心方法,CTFrameDraw 方法的作用是将指定的文本框架对象绘制到图形上下文中,实现文本的可视化呈现。

具体做法:在继承UIView的子类中,重写drawrect方法,里面最重要的一行就是CTFrameDraw(frame, ctx),即可完成渲染:

/// 绘制
override func draw(_ rect: CGRect) {guard let frame = frameRef, let ctx = UIGraphicsGetCurrentContext() else {return}ctx.textMatrix = CGAffineTransform.identityctx.translateBy(x: 0, y: bounds.size.height)ctx.scaleBy(x: 1.0, y: -1.0)CTFrameDraw(frame, ctx)
}

除了 CTFrameDraw , 还想要对文本内容有更精细的控制,可以使用CTLineDraw,CTRunDraw

  • void CTLineDraw(CTLineRef line,CGContextRef context )

  • void CTRunDraw( CTRunRef run, CGContextRef context,CFRange range ) 

CTLineDraw 函数绘制的是单行文本,需要在CGContext中设置好position,在图文混排时,可以用到。

CTRunDraw 函数绘制的是单个文本运行,YYText使用的渲染方法就是CTRunDraw,对于控制特别精细的可以但是CTRun控制渲染。

以下是在学习的过程中找到的资料:

CoreText的基础知识了解:

CoreText实战讲解,手把手教你实现图文、点击高亮、自定义截断功能 - 简书

文字排版入门—— 排版基础、CoreText和图文混排-腾讯云开发者社区-腾讯云

iOS 基于CoreText的排版引擎 - 简书

比较完整的txt阅读器demo:

iOS: .txt 小说阅读器功能开发的 5 个老套路 - 掘金

套路继续, .txt 小说阅读器功能开发 - 掘金

最简版demo: 使用coretext计算分页并渲染,上面demo的功能多,导致核心逻辑淹没在业务代码中,找起来麻烦,所以做了一个只展示核心原理的最简demo : 

博客园系列文章:

https://www.cnblogs.com/summer-blog/p/6030641.html

https://www.cnblogs.com/summer-blog/p/6030885.html

https://www.cnblogs.com/summer-blog/p/6044118.html

https://www.cnblogs.com/summer-blog/p/6402664.html

比较精细的阅读器思路,页面行高重排,目前我们还用不到

我在七猫做阅读器——排版篇

从基础的各种CoreText渲染,到页面之间切换动画都有独立的demo,最后有一个把CoreText渲染+页面切换集成在一起的demo 

小说阅读器的设计和实现 - 简书

阅读器多种翻页的设计与实现 - 简书 

GitHub - loyinglin/LearnCoreText

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/212696.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JVM 内存分析工具 Memory Analyzer Tool(MAT)的深度讲解

目录 一. 前言 二. MAT 使用场景及主要解决问题 三. MAT 基础概念 3.1. Heap Dump 3.2. Shallow Heap 3.3. Retained Set 3.4. Retained Heap 3.5. Dominator Tree 3.6. OQL 3.7. references 四. MAT 功能概述 4.1. 内存分布 4.2. 对象间依赖 4.3. 对象状态 4.4…

鸿蒙前端开发-构建第一个ArkTS应用(Stage模型)

创建ArkTS工程 若首次打开DevEco Studio&#xff0c;请点击Create Project创建工程。如果已经打开了一个工程&#xff0c;请在菜单栏选择File > New > Create Project来创建一个新工程。 选择Application应用开发&#xff08;本文以应用开发为例&#xff0c;Atomic Serv…

docker-compose安装教程

1.确认docker-compose是否安装 docker-compose -v如上图所示表示未安装&#xff0c;需要安装。 如上图所示表示已经安装&#xff0c;不需要再安装&#xff0c;如果觉得版本低想升级&#xff0c;也可以继续安装。 2.离线安装 下载docker-compose安装包&#xff0c;上传到服务…

人工智能原理复习--机器学习

文章目录 上一篇机器学习概述归纳(示例)学习ID3决策树算法K近邻算法下一篇 上一篇 人工智能原理复习–搜索策略&#xff08;二&#xff09; 机器学习概述 学习系统的基本结构&#xff1a; #mermaid-svg-JMjIZHjVOirLolvu {font-family:"trebuchet ms",verdana,ari…

辨析旅行商问题(TSP)与车辆路径问题(VRP)

目录 前言旅行商问题 (TSP)问题介绍数学模型符号定义问题输入约束条件目标函数问题输出 解的空间解空间大小计算解释 车辆路径问题 (VRP)问题介绍TSP到VRP的过渡数学模型符号定义问题输入约束条件优化目标问题输出 解空间特殊情况一般情况 TSP 与 VRP 对比 前言 计划是通过本文…

基于JavaWeb+SSM+Vue助农扶贫微信小程序系统的设计和实现

基于JavaWebSSMVue助农扶贫微信小程序系统的设计和实现 源码获取入口Lun文目录前言主要技术系统设计功能截图 源码获取入口 Lun文目录 目 录 第一章 绪论 1 1.1 研究背景 1 1.2 研究意义 1 1.3 研究内容 2 第二章 开发环境与技术 3 2.1 JSP技术 3 2.2 MySQL数据库 3 2.3 Java…

基于Solr的全文检索系统的实现与应用

文章目录 一、概念1、什么是Solr2、与Lucene的比较区别1&#xff09;Lucene2&#xff09;Solr 二、Solr的安装与配置1、Solr的下载2、Solr的文件夹结构3、运行环境4、Solr整合tomcat1&#xff09;Solr Home与SolrCore2&#xff09;整合步骤 5、Solr管理后台1&#xff09;Dashbo…

微服务学习:Nacos配置中心

先打开Nacos&#xff08;详见微服务学习&#xff1a;Nacos微服务架构中的服务注册、服务发现和动态配置&Nacos下载&#xff09; 1.环境隔离&#xff1a; 新建命名空间&#xff1a; 记住命名空间ID&#xff1a; c82496fb-237f-47f7-91ed-288a53a63324 再配置 就可达成环…

docker-centos中基于keepalived+niginx模拟主从热备完整过程

文章目录 一、环境准备二、主机1、环境搭建1.1 镜像拉取1.2 创建网桥1.3 启动容器1.4 配置镜像源1.5 下载工具包1.6 下载keepalived1.7 下载nginx 2、配置2.1 配置keepalived2.2 配置nginx2.2.1 查看nginx.conf2.2.2 修改index.html 3、启动3.1 启动nginx3.2 启动keepalived 4、…

【HarmonyOS开发】控件开发过程中,知识点记录

1、问题记录及解决方案 1.1 资源&#xff08;Icon&i18n&#xff09;问题 控件&#xff1a;只有一个JS文件&#xff0c;不会将任何资源型文件&#xff08;图片、字体、默认文字等&#xff09;打包到SO中。因此&#xff0c;当我们开发控件时&#xff0c;需要将需要使用到的资…

【机器学习】042_迁移学习

一、概述、定义 目的&#xff1a; 迁移学习的目的是将某个领域或任务上学习到的模式、知识应用到不同但相关的领域里&#xff0c;获取更多数据&#xff0c;而不必投入许多时间人力来进行数据的标注。 举例&#xff1a; 已经会下中国象棋&#xff0c;就可以类比着来学习国际…

Navicat连接Oracle数据库

Navicat连接Oracle数据库 打开服务里面找到Oracle服务 OracleServerXE或者OracleServerTTL 创建数据库连接 连接名默认自己起 主机选择本地 端口默认 服务名在服务中可以找到输入后缀 用户名默认都是system 密码是创建oracle时候填写的口令 点击测试连接即可

Spring Boot中的事务是如何实现的?懂吗?

SpringBoot中的事务管理&#xff0c;用得好&#xff0c;能确保数据的一致性和完整性&#xff1b;用得不好&#xff0c;可能会给性能带来不小的影响哦。 基本使用 在SpringBoot中&#xff0c;事务的使用非常简洁。首先&#xff0c;得感谢Spring框架提供的Transactional注解&am…

【金融数据分析】计算沪深300指数行业权重分布并用饼图展示

前言 前面的文章我们已经介绍了如何获取沪深300成分股所述行业以及权重的数据&#xff0c;想要了解这部分内容的小伙伴可以阅读上一篇文章 springbootjdbcTemplatesqlite编程示例——以沪深300成分股数据处理为例-CSDN博客 那么有了上文获取的数据&#xff0c;我们实际上可以…

【rabbitMQ】rabbitMQ控制台模拟收发消息

目录 1.新建队列 2.交换机绑定队列 3.查看消息是否到达队列 总结&#xff1a; 1.新建队列 2.交换机绑定队列 点击amq.fonout 3.查看消息是否到达队列 总结&#xff1a; 生产者&#xff08;publisher&#xff09;发送消息&#xff0c;先到达交换机&#xff0c;再到队列&…

期末速成数据库极简版【存储过程】(5)

目录 【7】系统存储过程 【8】用户存储过程——带输出参数的存储过程 创建存储过程 存储过程调用 【9】用户存储过程——不带输出参数的存储过程 【7】系统存储过程 系统存储我们就不做过程讲解用户存储过程会考察一道大题&#xff0c;所以我们把重点放在用户存储过程。…

U4_1 语法分析之自顶向下分析

文章目录 一、定义1、任务2、对比3、方法4、自顶向下面临问题 二、自顶向下分析1、概念2、特点3、二义性问题4、左递归问题1&#xff09;概念2&#xff09;消除3&#xff09;间接左递归 5、回溯问题1&#xff09;概念2&#xff09;消除3&#xff09;解决方法 6、总结 三、递归子…

【Proteus仿真】【51单片机】光照强度检测系统

文章目录 一、功能简介二、软件设计三、实验现象联系作者 一、功能简介 本项目使用Proteus8仿真51单片机控制器&#xff0c;使共阴数码管&#xff0c;PCF8591 ADC模块、光敏传感器等。 主要功能&#xff1a; 系统运行后&#xff0c;数码管显示光传感器采集光照强度值&#xff…

Gitzip插件【Github免翻下载】

今天给大家推荐一个github下载的插件&#xff0c;平常大家下载应该无外乎就是以下两种&#xff1a; Download zip利用git clone 但是这两种各有各的弊端&#xff0c;前者一般需要科学上网才可以&#xff0c;后者下载不稳定经常中途断掉。 今天给推荐一个款浏览器插件-Gitzip.大…

基于SSM的java衣服商城

基于SSM的java衣服商城 一、系统介绍二、功能展示四、其他系统实现五、获取源码 一、系统介绍 项目类型&#xff1a;Java EE项目 项目名称&#xff1a;基于SSM的美衣商城 项目架构&#xff1a;B/S架构 开发语言&#xff1a;Java语言 前端技术&#xff1a;Layui等 后端技术…