目标

  1. 使用 TextKit 快速分页
  2. 使用 UIPageViewController

支持平台

iOS, iPadOS
也许还支持 Mac Calalyst ?

使用语言

Swift

视图结构

|- UIViewController // 根视图, 可添加菜单显示, 手势操作等|- UIPageController // 章节视图, 一页对应一章| - UIPageController // 章节内容分页视图, 将单章内容进行分页显示|   | - UIViewController // 单页显示视图, 对应单页数据|   |   |- UITextView // 文字视图|   ||   | - UIViewController|   |   |- UITextView|   ||   | ...|| - UIPageController|   | - UIViewController|   |   |- UITextView|   ||   | - UIViewController|   |   |- UITextView|   ||   | ...|| ...

小说系统源码的章节内容分页视图中, 只要在返回单页显示视图的代理中返回 nil, 即可实现章节内容翻到最后一页时, 继续翻页翻到下一章节的逻辑

分页实现

首先, 一定要先确定好 TextView 的大小与内容间距, 即文字显示区域的大小, 这将严重影响到分页后的数据能不能正常显示

其次, 首行缩进最好用空格代替, 而不是用 NSParagraphStylefirstLineHeadIndent 属性来实现, 否则会出现某段落从中间被分开, 下一页依然被缩进的情况

首行缩进的空格数量可用以下逻辑计算:

let normalWidth = "你好".size(font: textFont).width // 请根据内容语言改变文字
let speaceWidth = " ".size(font: textFont).width // 一个空格的宽
let speaceCount = Int(normalWidth / speaceWidth)
let speace = String(repeating: " ", count: speaceCount)

然后在每段前添加空格

let result = content.string.components(separatedBy: "\n").map { "\(speace)\($0)" }

这样就可以在每段首行添加一个合适的缩进了

接下来就是重点的分页了


第一步, 前期参数准备:

  1. 准备好处理完成的 NSAttributedString, 最好包含各种字体, 颜色, 格式等设置信息, 避免分页视图拿到数据后再次生成 NSAttributedString , 重复设置内容样式导致的分页不准的情况

  2. 准备好文字显示区域大小的参数


第二步, 开始分页:
准备数据:

// 创建 NSLayoutManager, 所有的分页逻辑开端
let layoutManager = NSLayoutManager()// 如果没有给特定部分文字区域设置单独的布局, 可设置此项为 false, 以提高性能
layoutManager.allowsNonContiguousLayout = false// 使用之前准备好的 NSAttributedString 进行初始化 NSTextStorage
let textStorage = NSTextStorage(attributedString: string)
textStorage.addLayoutManager(layoutManager)// 设定文字显示区域参数
let viewSize: CGSize = CGSize(width: textAreaWidth, height:  textAreaHeight)// 设定 textView 的内间距
let textInsets = UIEdgeInsets.zero
let textViewFrame = CGRect(x: 0, y: 0, width: viewSize.width, height: viewSize.height)// 开始分页
var glyphRange: Int = 0
var numberOfGlyphs: Int = 0

分页循环:

var ranges: [NSRange] = []
repeat {let textContainer = NSTextContainer(size: viewSize)layoutManager.addTextContainer(textContainer)// 不断创建 textView 让 NSLayoutManager 进行内容分页let textView = UITextView(frame: textViewFrame, textContainer: textContainer)textView.isEditable = falsetextView.isSelectable = falsetextView.textContainerInset = textInsetstextView.showsVerticalScrollIndicator = falsetextView.showsHorizontalScrollIndicator = falsetextView.isScrollEnabled = false // 禁止滑动, 否则计算结果将不再准确textView.bounces = falsetextView.bouncesZoom = false// 获取当前分页内容所在位置let range = layoutManager.glyphRange(for: textContainer)ranges.append(range)// 判定是否分页完成glyphRange = NSMaxRange(range)numberOfGlyphs = layoutManager.numberOfGlyphs
} while glyphRange < numberOfGlyphs - 1

CoreText 版本:

var ranges: [NSRange] = []
let framesetter = CTFramesetterCreateWithAttributedString(string)
var textPosition = 0
while textPosition < string.length {let path = CGPath(rect: textViewFrame.inset(by: textInsets), transform: nil)let frame = CTFramesetterCreateFrame(framesetter, .init(location: textPosition, length: 0), path, nil)let stringRange = CTFrameGetVisibleStringRange(frame)let range: NSRange = .init(location: stringRange.location, length: stringRange.length)textPosition += stringRange.lengthranges.append(range)
}

至此, 就得到了带有格式的全文 NSAttributedString, 和分页区域的 ranges


第三步, 显示分页数据,小说系统源码的章节内容分页视图中, 将单章的 NSAttributedString 和分到的 range 分配给每一个单页显示视图, 在 UITextView 中直接设置 attributedTextattributedString.attributedSubstring(from: range)

UITextView 的设置务必于分页循环时的 UITextView 保持一致
声明:本文由云豹科技转发自Norld博客,如有侵权请联系作者删除

搭建小说系统源码,如何实现读书的分页功能相关推荐

  1. java计算机毕业设计在线小说系统源码+系统+mysql数据库+lw文档

    java计算机毕业设计在线小说系统源码+系统+mysql数据库+lw文档 java计算机毕业设计在线小说系统源码+系统+mysql数据库+lw文档 本源码技术栈: 项目架构:B/S架构 开发语言:Ja ...

  2. ptcms模板自动采集小说系统源码

    介绍: ptcms模板自动采集小说系统源码安装教程: mysql,php5.6 必须安装扩展:ZendGuardLoader 脚本解密 ionCube 脚本解密 fileinfo 通用扩展 opcac ...

  3. 最新4合1小说系统源码 (音乐、漫画、视频自动采集)

    软件介绍:  最新4合1小说系统源码 (音乐.漫画.视频自动采集),带完整教程,适合萌新运营,欢迎下载使用. 下载地址:https://download.csdn.net/download/m0_71 ...

  4. PACS/RIS系统源码 支持专业三维影像后处理功能,支持海量数据存储、迁移管理

    PACS系统源码 支持专业三维影像后处理功能,系统有演示和自主知识产权. 私信了解更多内容! 系统主要功能: (一)影像处理 1.数据接收功能:接收.获取影像设备的DICOM3.0和非DICOM3.0 ...

  5. 婚恋交友系统源码开发容易忽视的定位功能

    本文旨在阐释清楚婚恋交友系统源码的GPS相关开发过程中经常遇到的一些问题,涉及GPS版本变化.定位获取失败等常见问题:以减少大家信息获取的负担为宗旨.GPS本身并不复杂,但是因为GPS定位慢外加一些限 ...

  6. 最新帝国CMS花生小说系统源码+花生日记引流导航模板+带采集工具

    正文: 一个专注小说阅读细化分类的网站. 说明:小说公众号导航站模板,小说网站导航引流网站源码,网站结构清晰优化得好容易收录. 本站带火车自动采集,带手机端,并且有同步生成插件,采用帝国cms7.5内 ...

  7. 最新PTCMS小说系统源码_精美多风格四套全新版源码_模板.zip

    下载地址:http://sucaiip.com/company-275.html

  8. 2023最新UI拉拉米抢单发单系统源码/二开带教程/放量功能

    正文: 服务器系统:Linux+宝塔 亲测环境:Nginx1.16.1+PHP5.6+Mysql5.5 修改数据库配置文件:/config/database.php 运行目录设置:/public 短信 ...

  9. 最新74cms骑士人才招聘系统源码SE版V3.16.0/功能强大

    正文: 74cms骑士人才招聘系统是一项基于PHP+MYSQL为核心开发的一套开源专业人才招聘系统. 骑士人才系统拥有十多年的人才招聘系统运营解决方案,同时我们提供智能化招聘系统.招考系统等全方位系统 ...

最新文章

  1. Google 选择 Jetty 放弃 Tomcat
  2. 音视频互动开发平台之AnyChat SDK
  3. 活动 | Daung~!他们用产品思维改变医疗挂号问题
  4. 如何改变本地git的根目录
  5. 北斗信号服务器解算,GPS/北斗定位解算算法的研究
  6. Asp.net在线备份、压缩和修复Access数据库
  7. 12864液晶使用例程
  8. (95)分频器设计(偶数分频)
  9. 深度学习之dropout
  10. 测试~在使用共通处理时,需要注意的问题 ~ 使用前,清空Form中的值。
  11. 面向对象之自动绑定数据源
  12. [渝粤教育] 重庆电子工程职业学院 物联网工程导论 参考 资料
  13. WINX新增(1): KMP字符串查找算法
  14. 华为路由器怎么配置虚拟服务器,华为路由器配置实例详细备注讲解
  15. 社会化分享(附源码)
  16. 人工智能发展神速?37年前的尘封档案告诉你并没有
  17. Unity3D 模型描边插件Outline Effect详细使用说明、C#功能扩展
  18. 关注电动汽车能效水平 提高续航能力
  19. 图像处理(一):傅里叶变换简单讲解
  20. 牛顿迭代法求一个数的平方根(python)

热门文章

  1. 长沙有哪些比较有名的互联网公司?
  2. Lucene.net(4.8.0)+PanGu分词器 问题记录一 分词器Analyzer的构造和内部成员ReuseStategy
  3. 使用Arduino和GSM模块进行呼叫和消息
  4. 软件工程23种设计模式全解析
  5. AspectJ使用实例
  6. 武田将在即将召开的虚拟科学大会上呈报数据,强调肿瘤产品阵容和后续产品管线的广度
  7. 基于OHCI的USB主机 —— UFI读容量代码
  8. 关于移动通讯的发展史及5G的各项技术与面临的挑战阐述
  9. 周星驰经典对白 (不可不看哦)[转贴]
  10. 疯狂Java讲义(十三)----第五部分