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对象,可以直接强转;

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.rhkb.cn/news/218603.html

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

相关文章

JVM学习之JVM概述

JVM的整体结构 Hotspot VM是目前市面上高性能虚拟机代表作之一 它采用解释器与即时编译器并存的架构 在今天&#xff0c;Java程序的运行性能已经达到了可以和C/C程序一较高下的地步 Java代码执行流程 具体图为 JVM架构模型 Java编译器输入的指令流基本上是一种基于 栈的指令…

大创项目推荐 垃圾邮件(短信)分类算法实现 机器学习 深度学习

文章目录 0 前言2 垃圾短信/邮件 分类算法 原理2.1 常用的分类器 - 贝叶斯分类器 3 数据集介绍4 数据预处理5 特征提取6 训练分类器7 综合测试结果8 其他模型方法9 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 垃圾邮件(短信)分类算…

Github与Gitlab

学习目标 能够使用GitHub创建远程仓库并使用能够安装部署GitLab服务器能够使用GitLab创建仓库并使用掌握CI/CD的概念掌握蓝绿部署, 滚动更新,灰度发布的概念 GitHub是目前最火的开源项目代码托管平台。它是基于web的Git仓库&#xff0c;提供公有仓库和私有仓库&#xff0c;但私…

Amortized Bootstrapping of LWE:使用 BFV 打包处理

参考文献&#xff1a; [AP13] Alperin-Sheriff J, Peikert C. Practical bootstrapping in quasilinear time[C]//Annual Cryptology Conference. Berlin, Heidelberg: Springer Berlin Heidelberg, 2013: 1-20.[MS18] Micciancio D, Sorrell J. Ring packing and amortized F…

下午好~ 我的论文【CV边角料】(第三期)

文章目录 CV边角料Pixel ShuffleSENetCBAMGlobal Context Block (GC)Criss-Cross Attention modules (CC) CV边角料 Pixel Shuffle Real-Time Single Image and Video Super-Resolution Using an Efficient Sub-Pixel Convolutional Neural Network pixelshuffle算法的实现流…

初识GroovyShell

文章目录 前言一、GroovyShell二、maven三、解决方案四、关键代码4.1 数据库配置表(pg)4.2 入参4.3 分页查询 总结 前言 项目背景&#xff1a;查询多个表的数据列表和详情&#xff0c;但不想创建过多的po、dao、resp等项目文件。 一、GroovyShell Apache Groovy是一种强大的…

rabbitmq-windows安装使用-简易后台界面-修改密码

文章目录 1.下载2.安装3.安装 RabbitMQ4.后台访问5.修改密码 1.下载 将erlang运行时和rabbitmq-windows版本&#xff0c;上传在csdn&#xff0c;下载链接。https://download.csdn.net/download/m0_67316550/88633443 2.安装 右键&#xff0c;以管理员身份运行rabbitmq。启动…

如何安装LUT预设?达芬奇/FCP/PR怎么安装LUT预设.cube格式文件的教程

在下载的LUT调色预设压缩文件包中&#xff0c;通常两个包含不同格式的LUT文件&#xff1a; .cube 和 .xmp 包含的 .cube 文件几乎与主流的视频编辑和色彩校正软件兼容&#xff0c;并且还可以在 Adobe Photoshop 等一些照片应用程序中使用。如果主要是将这些 LUT 用于视频剪辑项…

Redis-数据结构

参考资料 极客时间Redis&#xff08;亚风&#xff09; Redis数据结构 SDS sds(Simple Dynamic String) 字符串接结构体: struct --attribute_- ((-_packed__)) sdshdr8{uint8_t len&#xff1b;/* buf已保祥的字符串字节数&#xff0c;不包含结束标示*/uint8_t alloc&#…

day02-报表技术POI

1、基于模板导出列表数据 1.1、需求 按照以下样式导出excel 1.2、思路 首先准备一个excel模板&#xff0c;这个模板把复杂的样式和固定的内容先准备好并且放入到项目中&#xff0c;然后读取到模板后向里面放入数据。 1.3、实现 第一步&#xff1a;准备一个excel作为导出的…

AI 编程助手 Copilot:从对话中分析程序性能

大家好&#xff0c;我是木川 一、介绍 GitHub Copilot 是 GitHub 和 OpenAI 合作开发的一个 AI 辅助编程工具 官网地址&#xff1a;https://github.com/features/copilot 官方文档&#xff1a;https://docs.github.com/copilot 分析程序性能在对话功能中有提到 二、安装 在 VSC…

Ubuntu 常用命令之 ll 命令用法介绍

ll是ls -l的别名&#xff0c;用于在Ubuntu系统中列出目录的详细信息。ls命令用于列出目录内容&#xff0c;-l选项则以长格式显示&#xff0c;包括文件类型、权限、链接数、所有者、组、大小、最后修改时间以及文件或目录名。 这是ll命令的基本格式 ll [选项]... [文件]...这是…

Halcon参考手册异常检测知识总结

1.1异常检测介绍 本章将介绍如何使用基于深度学习的异常检测和全局上下文异常检测。通过这两种方法&#xff0c;我们想要检测图像是否包含异常(异常是指偏离正常的事物&#xff0c;未知的事物)。 异常检测或全局上下文异常检测模型学习无异常图像的共同特征。经过训练的模型将…

JS中call()、apply()、bind()改变this指向的原理

大家如果想了解改变this指向的方法&#xff0c;大家可以阅读本人的这篇改变this指向的六种方法 大家有没有想过这三种方法是如何改变this指向的&#xff1f;我们可以自己写吗&#xff1f; 答案是&#xff1a;可以自己写的 让我为大家介绍一下吧&#xff01; 1.call()方法的原理…

Linux---压缩和解压缩命令

1. 压缩格式的介绍 Linux默认支持的压缩格式: .gz.bz2.zip 说明: .gz和.bz2的压缩包需要使用tar命令来压缩和解压缩.zip的压缩包需要使用zip命令来压缩&#xff0c;使用unzip命令来解压缩 压缩目的: 节省磁盘空间 2. tar命令及选项的使用 命令说明tar压缩和解压缩命令 …

二分查找|双指针:LeetCode:2398.预算内的最多机器人数目

作者推荐 【动态规划】【广度优先】LeetCode2258:逃离火灾 本文涉及的基础知识点 二分查找算法合集 滑动窗口 单调队列&#xff1a;计算最大值时&#xff0c;如果前面的数小&#xff0c;则必定被淘汰&#xff0c;前面的数早出队。 题目 你有 n 个机器人&#xff0c;给你两…

锁--07_2---- index merge(索引合并)引起的死锁

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 案例分析生产背景死锁日志表结构执行计划 EXPLAN为什么会用 index_merge&#xff08;索引合并&#xff09;为什么用了 index_merge就死锁了解决方案注&#xff1a;M…

初识Pandas函数是Python的一个库(继续更新...)

学习网页&#xff1a; Welcome to Python.orghttps://www.python.org/https://www.python.org/https://www.python.org/ Pandas函数库 Pandas是一个Python库&#xff0c;提供了大量的数据结构和数据分析工具&#xff0c;包括DataFrame和Series等。Pandas的函数非常丰富&…

Spring Boot3.1.6配置对应的Swagger

1. pom.xml导入Swagger依赖 <!--swagger3--> <dependency><groupId>org.springdoc</groupId><artifactId>springdoc-openapi-starter-webmvc-ui</artifactId><version>2.0.2</version> </dependency> 2.创建SwaggerCo…

自动化访客互动:提升网站效益与用户体验的关键优势

在激烈的市场竞争环境中&#xff0c;想抢占市场&#xff0c;获得收益并不容易。每一个订单的完成都要经过一定的销售周期&#xff0c;所以企业可以根据销售周期每个阶段的特点进行优化&#xff0c;留住客户。其中&#xff0c;企业可以在与客户在线互动的过程中&#xff0c;让互…