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.pswp.cn/news/212696.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/212696.shtml
英文地址,請注明出處:http://en.pswp.cn/news/212696.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【原創】錄剪視頻的折騰之路

制作視頻的起因 本人為IT男&#xff0c;IT發展快&#xff0c;需要學習的東西又多。往往為了一個技術小問題&#xff0c;花好幾天時間學習&#xff0c;接下來十來分鐘把事情做完。下次遇到這個同樣的問題的時候&#xff0c;可能是幾個月后&#xff0c;甚至是幾年以后了。這些技…

微信小程序頁面跳轉方法

文章目錄 前言方式一&#xff1a;wx.navigateTo方式二&#xff1a;wx.redirectTo方式三&#xff1a;wx.reLaunch方式四&#xff1a;wx.switchTab方式五&#xff1a;wxml中navigator標簽跳轉頁面回退 前言 微信小程序頁面跳轉的各種方法總結&#xff0c;備查。 方式一&#xff…

汽車連接器

汽車連接器 電子元器件百科 文章目錄 汽車連接器前言一、汽車連接器是什么二、汽車連接器的類別三、汽車連接器的應用實例四、汽車連接器的作用原理總結前言 汽車連接器通常需要具備防水、防塵、耐高溫等特性,以適應汽車惡劣的工作環境。它們的設計和連接方式也各不相同,以適…

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;上傳到服務…

uniapp小程序分享為灰色

引用&#xff1a;https://www.cnblogs.com/panwudi/p/17074172.html uniapp開發的微信小程序&#xff0c;沒有轉發&#xff0c;分享&#xff1a; 創建一個mixin:common/share.js export default {onShareAppMessage(res) { //發送給朋友return {}},onShareTimeline(res) {//…

人工智能原理復習--機器學習

文章目錄 上一篇機器學習概述歸納(示例)學習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…

4-Docker命令之docker commit

1.docker commit介紹 docker commit命令是用于根據docker容器的改變創建一個新的docker鏡像 2.docker commit用法 docker commit [參數] container [repository[:tag]] [rootcentos79 ~]# docker commit --helpUsage: docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG…

微服務學習:Nacos配置中心

先打開Nacos&#xff08;詳見微服務學習&#xff1a;Nacos微服務架構中的服務注冊、服務發現和動態配置&Nacos下載&#xff09; 1.環境隔離&#xff1a; 新建命名空間&#xff1a; 記住命名空間ID&#xff1a; c82496fb-237f-47f7-91ed-288a53a63324 再配置 就可達成環…

vue3 創建過程中 運行npm create vue@latest 和 npm install卡住不動的解決方法之一

問題&#xff1a;npm create vuelatest、和npm install 不管是電腦cmd上還是vscode終端上都是卡很久或不動&#xff01; 解決&#xff1a; 1、查看npm代理 npm config get registry2、更換npm鏡像 npm config set registryhttps://registry.npmmirror.com這里換成淘寶源好像…

學習 Vue 3 源碼

Vue 3 是一款流行的前端框架&#xff0c;它的數據代理和虛擬 DOM 實現是其核心功能之一 Vue 3 的數據代理 在 Vue 3 中&#xff0c;數據代理是指將組件實例的屬性代理到其內部狀態對象上。這使得開發者可以使用更便捷的方式來訪問和修改組件的狀態。 Vue 3 的數據代理實現主…

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;就可以類比著來學習國際…

Java單元測試:JUnit和Mockito的使用指南

引言&#xff1a; 在軟件開發過程中&#xff0c;單元測試是一項非常重要的工作。通過單元測試&#xff0c;我們可以驗證代碼的正確性、穩定性和可維護性&#xff0c;幫助我們提高代碼質量和開發效率。本文將介紹Java中兩個常用的單元測試框架&#xff1a;JUnit和Mockito&#x…

Navicat連接Oracle數據庫

Navicat連接Oracle數據庫 打開服務里面找到Oracle服務 OracleServerXE或者OracleServerTTL 創建數據庫連接 連接名默認自己起 主機選擇本地 端口默認 服務名在服務中可以找到輸入后綴 用戶名默認都是system 密碼是創建oracle時候填寫的口令 點擊測試連接即可