21 Commits
Author SHA1 Message Date
shenlei 68d9363f0a feat: optimize reader caching and document formats 2026-07-20 17:22:06 +09:00
shenandshen 52f803e8db fix: restore demo build and drawing interactions 2026-07-15 21:13:05 +08:00
shenleiandClaude Fable 5 1422461224 将 PDF 成品阅读控制器下沉为 RDPDFReaderView SDK,并修复手机横屏单页连续竖滑
SDK 化收尾:
- 新增 RDPDFReaderView 本地 pod(阅读容器、成品控制器、划线/注释、画笔、
  目录/设置面板、主题与持久化契约),Demo 精简为 PDFKit 页面提供者 + 持久化宿主
- Podfile/工程接入 RDPDFReaderView 与 SnapKit,示例书改用 PDF 阅读器示例文件

手机横屏单页竖滑修复:
- cell 高度改为宽度适配后的纸张高度(sizeForItemAt + 宿主按页提供纵横比),
  纵向超出一屏的内容由外层 collectionView 连续滚动承接,不再被裁掉
- 页面内部不再开启同向嵌套的兜底竖向拖动,修复外层滚动被吞、无法滚到下一页
- 修复 applyContentSize 先评估拖动开关后写 contentSize 的顺序缺陷
- 页码按视口中心命中 cell 计算,补 didEndDragging 兜底;旋转重锚点与
  transitionToPage 改用真实布局位置
- 横竖屏同为竖滑时也重建 cell 并刷新全部可见页的适配配置
- 横屏单页点击不再跳页,只显隐工具栏;翻页交给连续竖滑

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-15 18:49:44 +09:00
shenleiandClaude Fable 5 a17164d1a9 将 PDF 注释编辑器、笔记列表、目录/设置面板与面板呈现下沉到 SDK
SDK 新增:
- RDPDFReaderAnnotationEditorViewController:注释输入页(UI 与 EPUB 编辑器一致)
- RDPDFReaderAnnotationListViewController:笔记列表(全部/注释/高亮筛选、滑动删除)
- RDPDFReaderNavigationPanelViewController:目录/书签/缩略图面板容器
- RDPDFReaderSettingsPanelViewController:亮度/翻页方式/主题设置面板
- RDPDFReaderPanelPresenter + 转场/呈现控制器:底部面板统一呈现,
  转场代理生命周期由被呈现控制器持有,宿主无需保存引用
- RDPDFReaderThemeOption:SDK 自有主题模型,面板与编辑器不再依赖 EPUB 主题类型
- UIColor(rdPDFHexString:) 公开,并消除覆盖层内的私有重复实现

Demo 侧:
- 删除 PDFDemoAnnotationUI.swift 整个文件
- 删除 PDFDemoNavigationViewController / PDFDemoSettingsViewController /
  面板转场三个类(约 410 行),改为调用 SDK 组件
- ThemePreset 增加到 RDPDFReaderThemeOption 的映射

无障碍标识全部保留,UI 自动化不受影响。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-15 10:51:51 +09:00
shenleiandClaude Fable 5 1bde945d36 将 PDF 划线/注释交互完整下沉到 SDK 库
- RDPDFReaderPageView:完整页面视图移入 SDK(缩放画布、文字选择层、
  选区放大镜、无障碍视口),并内置与 EPUB 一致的两套系统编辑菜单:
  选区菜单(复制/划线/注释)、已有划线菜单(复制/注释|删除注释/删除划线)
- RDPDFReaderPageViewDelegate:宿主只通过代理做数据持久化与打开业务界面
- 修复划线命中判定:点击点先归一化再与标注比例坐标比较
- 修复 tappedHighlight 残留导致选区菜单项被错误过滤的问题
- 主题应用改为纯颜色入参,SDK 不依赖 EPUB 主题类型
- RDPDFReaderHighlightOverlayView 恢复为纯绘制层,点击交互统一由页面视图处理
- Demo 删除 PDFDemoPageView(约 290 行),改为实现代理回调

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-15 10:33:43 +09:00
shenleiandClaude Haiku 4.5 5a19cfde14 refactor: move highlight menu logic from Demo to SDK library
Add RDPDFReaderPageView as base page view class in SDK:
- Handles all highlight menu interactions and display
- Provides RDPDFReaderPageViewDelegate for menu action callbacks
- Unifies with EPUB architecture: RDEPUBTextContentView pattern
- Reduces code duplication for applications using the SDK

Benefits:
- Reusable menu interaction logic across all PDF apps
- Consistent architecture with EPUB reader
- Single responsibility principle (UI in library, persistence in app)
- Backward compatible (existing code continues to work)

Applications now implement RDPDFReaderPageViewDelegate to:
- Respond to highlight menu actions
- Handle data persistence
- Refresh UI after changes

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-07-15 10:19:54 +09:00
shenleiandClaude Haiku 4.5 7c16647306 fix: remove duplicate canPerformAction method and merge with existing implementation
- Keep single canPerformAction method that handles both text selection menu and highlight menu
- Check tappedHighlight first, then fall back to text selection logic
- Resolves method override conflict

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-07-15 10:16:30 +09:00
shenleiandClaude Haiku 4.5 d56e99a83f add tap-on-highlight menu with copy/annotate/delete actions, matching EPUB behavior
- Add RDPDFReaderAnnotationMenuAction and RDPDFReaderExistingHighlightMenuAction enums to PDF contracts
- Enable highlight overlay view to detect tap gestures and report tapped highlights
- Implement existing highlight menu in Demo page: copy text, add/edit annotation, delete annotation, delete underline
- Handle menu actions in PDFDemoReaderViewController to persist changes
- Ensure highlight tap detection doesn't interfere with text selection

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-07-15 10:06:02 +09:00
shenleiandClaude Haiku 4.5 fed34da246 unify PDF and EPUB reader: convert highlights to underlines, improve annotation UI, change paging from horizontal to vertical
- Convert PDF highlights from filled rectangles to underlines matching EPUB style
- Update PDF annotation editor with EPUB-style design: gold sidebar rail, semi-transparent background, improved spacing and shadows, dark mode support
- Change PDF page-curl paging from left-right horizontal to up-down vertical: update gesture recognition and tap regions accordingly

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-07-15 10:01:23 +09:00
shenlei 72c1f8d89c 扩展顶部工具栏业务按钮能力 2026-07-13 16:16:31 +09:00
shenlei 822949f6cc 清理重复 Podfile 配置 2026-07-13 16:09:45 +09:00
shenlei 6a0a1223b1 完善阅读器主题与批注界面 2026-07-13 13:01:14 +09:00
shenlei adc24d3f36 fix: preserve images in dark reader theme 2026-07-13 12:30:14 +09:00
shenandshen 8ccb7157b2 feat: improve epub reader controls and annotations 2026-07-12 12:23:10 +08:00
shenandshen 063a493b18 fix epub reader playback and trial navigation 2026-07-11 08:16:56 +08:00
shenlei d7fcda345d refactor: rename RDReaderView -> RDEpubReaderView, update pod config and docs
- Rename source module from RDReaderView to RDEpubReaderView
- Move all source files from Sources/RDReaderView/ to Sources/RDEpubReaderView/
- Update podspec: RDReaderView.podspec -> RDEpubReaderView.podspec
- Update Podfile, demo project, and CocoaPods config for new pod name
- Delete old RDReaderView pod support files from ReadViewDemo/Pods
- Add new RDEpubReaderView pod support files
- Update documentation (API ref, architecture, UML, conventions, etc.)
- Add FixedLayoutRotationTests
- Update .gitignore: exclude .DS_Store, manual unpack backups, _ssoft-output
2026-07-10 19:44:53 +09:00
shenleiandClaude Fable 5 d5a7755702 feat: 支持加密 EPUB、试读墙、工具栏定制与朝向锁定
- 新增 RDEPUBResourceDataProvider 解密钩子协议 + 访问登记表,收敛章节 HTML/
  图片/内联 CSS/脚注/封面/图片查看器/正文取图等读取点,scheme handler 对加密书
  禁用流式分支;新增 RDEPUBDecryptingImageAttachment 解密 DTCoreText 图片附件;
  RDEPUBReaderDependencies.live(resourceDataProvider:) 便捷注入。明文书零影响。
- 试读墙:configuration.trialPolicy + delegate epubReaderTrialWallView/
  DidReachTrialWall,UI 由宿主提供(RDEPUBReaderController+Trial)。
- 工具栏定制:RDEPUBReaderTop/BottomToolViewProtocol 协议 + dependencies 工厂注入,
  内置栏已 conform,configureTopToolView 改协议类型。
- 朝向锁定:RDEPUBMetadata.orientation(OPF rendition:orientation 主,
  iBooks display-options 兜底)+ RDEPUBReaderController+Orientation 重写支持朝向、
  打开后主动转向。

注:RDEPUBReaderController+LocationResolution / PaginationCoordinator 为本次改动前
即存在的工作区修改,一并纳入。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 12:25:17 +09:00
shenlei 83dfa40299 完善搜索定位与内存监测回归验证
本次提交围绕搜索链路的稳定性、定位恢复体验以及长章节内存优化的验证能力进行了补强。

主要改动:

1. 调整阅读器搜索栏、搜索协调器与定位恢复逻辑,改善搜索结果跳转、状态同步与相关 UI 行为。

2. 补充内存探针接入点与上下文记录,便于跟踪阅读过程中的内存占用变化。

3. 更新 RDURLReaderController、ReaderContext 与工具视图相关实现,使调试与观测链路更完整。

4. 新增 MemoryFootprintTests,并同步更新 DemoReaderState 与 SearchTests,用 UI 测试覆盖搜索与内存相关回归场景。
2026-07-09 18:46:04 +09:00
shenleiandClaude Fable 5 c4be426299 长章节内存优化 P1:章节级共享显示内容与 layouter,高亮转 overlay
- 新增 RDEPUBChapterDisplayContentCache(LRU 2,主线程限定,控制器
  持有):整章显示串构建时一次性注入全章主题色与暗黑图替换,配套
  共享 DTCoreTextLayouter;签名 = 章节内容对象标识 + 主题双色 +
  暗黑图配置,设置/主题变化自动失效重建
- RDEPUBTextContentView.configure 改为引用共享 entry,删除每次翻页
  的整章可变拷贝与按页属性写入
- 高亮/下划线改走 overlay decoration(高亮背景层、下划线前景层),
  移除 applyHighlightsToContent 与 render view 的属性绘制路径,
  高亮增删不再触发整章重建
- 内存警告时清空 display cache;shouldAvoidReaderPageCaching 保持
  保守(P1-4 待真机数据)

验证:编译通过;高亮/批注/选区/翻页/设置 13 项 UI 回归全过。
SearchTests 10 项失败经二分确认为先存回归(5a41066 与 e4e629a 均
复现,最后已知通过为 2026-06-08),与本次改动无关,待单独排查。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 16:09:40 +09:00
shenleiandClaude Fable 5 e4e629a06c 长章节内存优化 P0:每页子串改按需构造并新增内存打点
- RDEPUBTextPage.content 由存储属性改为基于 chapterContent+contentRange
  的计算属性,三处构造点不再生成每页子串,章节缓存窗口内每章少一份
  整章文本常驻副本
- 新增 RDEPUBMemoryProbe(--demo-memory-probe 开启),在章节插入、
  每 20 次翻页、设置失效、转屏完成时输出 phys_footprint
- shouldAvoidReaderPageCaching 补注释,锁定放开前置条件为 P1-1
- 新增实施清单文档 LONG_CHAPTER_MEMORY_OPTIMIZATION_PLAN.md 并勾选 P0

验证:Demo 编译通过;UI 回归 9/9 通过(PageNavigation/
ReaderOpenClose/LargeBookOnDemand)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 15:34:45 +09:00
shenlei 5a41066b66 修复 EPUB 文本分页显示错位并补充分页调试能力
本次提交围绕 DTCoreText 文本页的分页一致性、交互索引和高亮命中进行了集中修复。

主要改动:

1. 文本页显示改为基于整章 attributed string 的上下文布局,只对当前页 range 进行渲染,避免页面子串重新换行导致的页末断行偏差。

2. 页面布局快照与交互控制器统一改为使用 chapter-absolute 索引,修正点击、选区、菜单锚点与高亮矩形在整章上下文下的定位。

3. 修复跨章节高亮串页问题,并调整文本页高亮命中逻辑:保留 CoreText 层绘制,点击时按高亮真实 rect 精确命中,避免重复绘制和整行误判。

4. 收紧 reader 级页面缓存策略,避免预加载同时持有多份整章显示副本带来的内存放大。

5. 新增分页边界校验器、垂直对齐器和 settings-flip 自动化调试入口,用于复现与诊断页范围/显示度量不一致问题。

6. 放宽 inline attachment 的 avoid-break 处理,并补充相关分页问题调查文档与索引。
2026-07-08 14:27:36 +09:00
621 changed files with 21551 additions and 7650 deletions
+32
View File
@@ -6,3 +6,35 @@ xcuserdata/
.artifacts/ .artifacts/
.deriveddata/ .deriveddata/
.swift-module-cache/ .swift-module-cache/
ReadViewDemo/build/
# This repository checks in the Demo Pods project. Keep generated files for the
# local RDEpubReaderView/RDPDFReaderView pods and vendored SnapKit visible even
# when a global gitignore excludes Pods.
!ReadViewDemo/Pods/
!ReadViewDemo/Pods/Local Podspecs/
!ReadViewDemo/Pods/Local Podspecs/RDEpubReaderView.podspec.json
!ReadViewDemo/Pods/Local Podspecs/RDPDFReaderView.podspec.json
!ReadViewDemo/Pods/Target Support Files/
!ReadViewDemo/Pods/Target Support Files/RDEpubReaderView/
!ReadViewDemo/Pods/Target Support Files/RDEpubReaderView/**
!ReadViewDemo/Pods/Target Support Files/RDPDFReaderView/
!ReadViewDemo/Pods/Target Support Files/RDPDFReaderView/**
!ReadViewDemo/Pods/SnapKit/
!ReadViewDemo/Pods/SnapKit/**
!ReadViewDemo/Pods/Target Support Files/SnapKit/
!ReadViewDemo/Pods/Target Support Files/SnapKit/**
# macOS system files
.DS_Store
# Local backup directories
*手动解包备份*
# AI tool output
_ssoft-output/
.claude/
# Python bytecode
__pycache__/
*.py[cod]
+2 -2
View File
@@ -1,7 +1,7 @@
# ReadViewSDK 代码审查报告 # ReadViewSDK 代码审查报告
> 审查日期:2026-06-26 > 审查日期:2026-06-26
> 审查范围:Sources/RDReaderView 全部源码 > 审查范围:Sources/RDEpubReaderView 全部源码
> 审查方法:逐文件阅读 + 交叉验证 + 线程模型分析 > 审查方法:逐文件阅读 + 交叉验证 + 线程模型分析
--- ---
@@ -148,4 +148,4 @@ func currentTextPageSize() -> CGSize {
| 🟡 P2 | P2-2 | javaScriptStringLiteral 实现依赖隐式假设 | 可维护性 | | 🟡 P2 | P2-2 | javaScriptStringLiteral 实现依赖隐式假设 | 可维护性 |
| 🔵 P3 | P3-1 | 后台线程同步 hop 主线程获取布局信息 | 线程模型 | | 🔵 P3 | P3-1 | 后台线程同步 hop 主线程获取布局信息 | 线程模型 |
**整体评价**: SDK 架构设计良好,模块分层清晰,引用管理(unowned/weak)使用正确,分页取消机制有 `paginationToken` + `cancellationController` 兜底。主要值得修复的是三个性能类 P1 问题(主线程阻塞搜索、资源加载阻塞、缓存无清理),两个 P2 鲁棒性/可维护性问题,以及一个 P3 线程模型优化建议。 **整体评价**: SDK 架构设计良好,模块分层清晰,引用管理(unowned/weak)使用正确,分页取消机制有 `paginationToken` + `cancellationController` 兜底。主要值得修复的是三个性能类 P1 问题(主线程阻塞搜索、资源加载阻塞、缓存无清理),两个 P2 鲁棒性/可维护性问题,以及一个 P3 线程模型优化建议。
+27 -27
View File
@@ -8,7 +8,7 @@
## 1. RDEPUBReaderController — 入口控制器 ## 1. RDEPUBReaderController — 入口控制器
**文件**`Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift` 及扩展 **文件**`Sources/RDEpubReaderView/EPUBUI/RDEPUBReaderController.swift` 及扩展
### 1.1 初始化 ### 1.1 初始化
@@ -180,7 +180,7 @@ func nativeTextSemanticSummary() -> String?
## 2. RDEPUBReaderDelegate — 委托协议 ## 2. RDEPUBReaderDelegate — 委托协议
**文件**`Sources/RDReaderView/EPUBUI/RDEPUBReaderDelegate.swift` **文件**`Sources/RDEpubReaderView/EPUBUI/RDEPUBReaderDelegate.swift`
所有方法均有默认空实现,可按需实现。 所有方法均有默认空实现,可按需实现。
@@ -232,7 +232,7 @@ public protocol RDEPUBReaderDelegate: AnyObject {
## 3. RDEPUBReaderPersistence — 持久化协议 ## 3. RDEPUBReaderPersistence — 持久化协议
**文件**`Sources/RDReaderView/EPUBUI/RDEPUBReaderPersistence.swift` **文件**`Sources/RDEpubReaderView/EPUBUI/RDEPUBReaderPersistence.swift`
```swift ```swift
public protocol RDEPUBReaderPersistence: AnyObject { public protocol RDEPUBReaderPersistence: AnyObject {
@@ -264,7 +264,7 @@ public protocol RDEPUBReaderPersistence: AnyObject {
## 4. RDEPUBReaderConfiguration — 配置模型 ## 4. RDEPUBReaderConfiguration — 配置模型
**文件**`Sources/RDReaderView/EPUBUI/Settings/RDEPUBReaderConfiguration.swift` **文件**`Sources/RDEpubReaderView/EPUBUI/Settings/RDEPUBReaderConfiguration.swift`
| 属性 | 类型 | 默认值 | 说明 | | 属性 | 类型 | 默认值 | 说明 |
|------|------|--------|------| |------|------|--------|------|
@@ -273,7 +273,7 @@ public protocol RDEPUBReaderPersistence: AnyObject {
| `fontChoice` | `RDEPUBReaderFontChoice` | `.system` | 字体选择(system/serif/rounded/monospaced | | `fontChoice` | `RDEPUBReaderFontChoice` | `.system` | 字体选择(system/serif/rounded/monospaced |
| `numberOfColumns` | `Int` | `1` | 每页列数(1 或 2 | | `numberOfColumns` | `Int` | `1` | 每页列数(1 或 2 |
| `columnGap` | `CGFloat` | `20` | 列间距 | | `columnGap` | `CGFloat` | `20` | 列间距 |
| `displayType` | `RDReaderView.DisplayType` | `.pageCurl` | 翻页模式 | | `displayType` | `RDEpubReaderView.DisplayType` | `.pageCurl` | 翻页模式 |
| `landscapeDualPageEnabled` | `Bool` | `true` | 横屏双页 | | `landscapeDualPageEnabled` | `Bool` | `true` | 横屏双页 |
| `showsTableOfContents` | `Bool` | `true` | 显示目录 | | `showsTableOfContents` | `Bool` | `true` | 显示目录 |
| `allowsHighlights` | `Bool` | `true` | 允许高亮 | | `allowsHighlights` | `Bool` | `true` | 允许高亮 |
@@ -301,50 +301,50 @@ public protocol RDEPUBReaderPersistence: AnyObject {
## 5. 翻页容器协议 ## 5. 翻页容器协议
**文件**`Sources/RDReaderView/ReaderView/RDReaderViewProtocols.swift` **文件**`Sources/RDEpubReaderView/ReaderView/RDEpubReaderViewProtocols.swift`
### 5.1 RDReaderPageProvider(推荐) ### 5.1 RDEpubReaderPageProvider(推荐)
```swift ```swift
@objc public protocol RDReaderPageProvider: NSObjectProtocol { @objc public protocol RDEpubReaderPageProvider: NSObjectProtocol {
/// ///
func numberOfPages(in readerView: RDReaderView) -> Int func numberOfPages(in readerView: RDEpubReaderView) -> Int
/// ///
/// - Parameters: /// - Parameters:
/// - index: 0-based /// - index: 0-based
/// - reusableView: nil /// - reusableView: nil
func readerView(_ readerView: RDReaderView, viewForPageAt index: Int, reusableView: UIView?) -> UIView func readerView(_ readerView: RDEpubReaderView, viewForPageAt index: Int, reusableView: UIView?) -> UIView
/// ///
@objc optional func pageIdentifier(in readerView: RDReaderView, index: Int) -> String? @objc optional func pageIdentifier(in readerView: RDEpubReaderView, index: Int) -> String?
/// ///
@objc optional func readerViewTopChrome(_ readerView: RDReaderView) -> UIView? @objc optional func readerViewTopChrome(_ readerView: RDEpubReaderView) -> UIView?
/// ///
@objc optional func readerViewBottomChrome(_ readerView: RDReaderView) -> UIView? @objc optional func readerViewBottomChrome(_ readerView: RDEpubReaderView) -> UIView?
} }
``` ```
### 5.2 RDReaderDelegate ### 5.2 RDEpubReaderDelegate
```swift ```swift
@objc public protocol RDReaderDelegate: NSObjectProtocol { @objc public protocol RDEpubReaderDelegate: NSObjectProtocol {
/// ///
func pageNum(readerView: RDReaderView, pageNum: Int) func pageNum(readerView: RDEpubReaderView, pageNum: Int)
/// ///
@objc optional func readerViewOrientationWillChange(readerView: RDReaderView, isLandscape: Bool) @objc optional func readerViewOrientationWillChange(readerView: RDEpubReaderView, isLandscape: Bool)
} }
``` ```
### 5.3 RDReaderPageNavigating ### 5.3 RDEpubReaderPageNavigating
```swift ```swift
public protocol RDReaderPageNavigating: AnyObject { public protocol RDEpubReaderPageNavigating: AnyObject {
/// ///
var currentPage: Int { get } var currentPage: Int { get }
@@ -357,19 +357,19 @@ public protocol RDReaderPageNavigating: AnyObject {
} }
``` ```
### 5.4 RDReaderDataSource(遗留) ### 5.4 RDEpubReaderDataSource(遗留)
```swift ```swift
@objc public protocol RDReaderDataSource: NSObjectProtocol { @objc public protocol RDEpubReaderDataSource: NSObjectProtocol {
func pageCountOfReaderView(readerView: RDReaderView) -> Int func pageCountOfReaderView(readerView: RDEpubReaderView) -> Int
func pageContentView(readerView: RDReaderView, pageNum: Int, containerView: UIView?) -> UIView func pageContentView(readerView: RDEpubReaderView, pageNum: Int, containerView: UIView?) -> UIView
func pageIdentifier(readerView: RDReaderView, pageNum: Int) -> String? func pageIdentifier(readerView: RDEpubReaderView, pageNum: Int) -> String?
@objc optional func topToolView(readerView: RDReaderView) -> UIView? @objc optional func topToolView(readerView: RDEpubReaderView) -> UIView?
@objc optional func bottomToolView(readerView: RDReaderView) -> UIView? @objc optional func bottomToolView(readerView: RDEpubReaderView) -> UIView?
} }
``` ```
通过 `RDReaderLegacyDataSourceAdapter` 自动适配到 `RDReaderPageProvider` 通过 `RDEpubReaderLegacyDataSourceAdapter` 自动适配到 `RDEpubReaderPageProvider`
--- ---
+25 -25
View File
@@ -22,7 +22,7 @@ ReadViewSDK 是一个 iOS EPUB 阅读器 SDK,支持文本重排(Reflowable
│ ReadViewDemo (Demo App) │ │ ReadViewDemo (Demo App) │
│ ViewController · LaunchAutomationPlan · UITests │ │ ViewController · LaunchAutomationPlan · UITests │
└──────────────────────────────┬──────────────────────────────┘ └──────────────────────────────┬──────────────────────────────┘
│ imports RDReaderView │ imports RDEpubReaderView
┌──────────────────────────────▼──────────────────────────────┐ ┌──────────────────────────────▼──────────────────────────────┐
│ EPUBUI 层 │ │ EPUBUI 层 │
│ RDEPUBReaderController · Coordinators · Settings · TextPage │ │ RDEPUBReaderController · Coordinators · Settings · TextPage │
@@ -30,8 +30,8 @@ ReadViewSDK 是一个 iOS EPUB 阅读器 SDK,支持文本重排(Reflowable
└──────────────────────────────┬──────────────────────────────┘ └──────────────────────────────┬──────────────────────────────┘
┌──────────────────────────────▼──────────────────────────────┐ ┌──────────────────────────────▼──────────────────────────────┐
│ RDReaderView 层 │ │ RDEpubReaderView 层 │
│ RDReaderView · FlowLayout · PreloadController │ │ RDEpubReaderView · FlowLayout · PreloadController │
│ SpreadResolver · TapRegionHandler · PagingController │ │ SpreadResolver · TapRegionHandler · PagingController │
└──────────────────────────────┬──────────────────────────────┘ └──────────────────────────────┬──────────────────────────────┘
@@ -54,7 +54,7 @@ ReadViewSDK 是一个 iOS EPUB 阅读器 SDK,支持文本重排(Reflowable
|---|---|---| |---|---|---|
| **EPUBCore** | EPUB 文件解析、资源管理、WebView 渲染、JS 桥接 | `RDEPUBParser`, `RDEPUBPublication`, `RDEPUBWebView` | | **EPUBCore** | EPUB 文件解析、资源管理、WebView 渲染、JS 桥接 | `RDEPUBParser`, `RDEPUBPublication`, `RDEPUBWebView` |
| **EPUBTextRendering** | HTML→NSAttributedString 转换、排版、分页、全书构建 | `RDEPUBTextBookBuilder`, `RDEPUBCoreTextPageFrameFactory` | | **EPUBTextRendering** | HTML→NSAttributedString 转换、排版、分页、全书构建 | `RDEPUBTextBookBuilder`, `RDEPUBCoreTextPageFrameFactory` |
| **RDReaderView** | 通用翻页容器、手势识别、页面预加载、双页布局 | `RDReaderView`, `RDReaderFlowLayout`, `RDReaderPreloadController` | | **RDEpubReaderView** | 通用翻页容器、手势识别、页面预加载、双页布局 | `RDEpubReaderView`, `RDEpubReaderFlowLayout`, `RDEpubReaderPreloadController` |
| **EPUBUI** | 阅读器控制器、协调器模式、设置面板、按需加载、磁盘缓存 | `RDEPUBReaderController`, `RDEPUBReaderPaginationCoordinator` | | **EPUBUI** | 阅读器控制器、协调器模式、设置面板、按需加载、磁盘缓存 | `RDEPUBReaderController`, `RDEPUBReaderPaginationCoordinator` |
--- ---
@@ -310,10 +310,10 @@ RDEPUBWebView (UIView)
--- ---
## 7. 翻页容器架构(RDReaderView ## 7. 翻页容器架构(RDEpubReaderView
``` ```
RDReaderView (UIView) RDEpubReaderView (UIView)
├─ 三种翻页模式: ├─ 三种翻页模式:
│ ├─ .pageCurl → UIPageViewController (翻页动画) │ ├─ .pageCurl → UIPageViewController (翻页动画)
@@ -321,14 +321,14 @@ RDReaderView (UIView)
│ └─ .verticalScroll → UICollectionView (垂直滚动) │ └─ .verticalScroll → UICollectionView (垂直滚动)
├─ 组合对象: ├─ 组合对象:
│ ├─ RDReaderPagingController // 翻页状态管理、请求队列 │ ├─ RDEpubReaderPagingController // 翻页状态管理、请求队列
│ ├─ RDReaderPreloadController // 页面预加载、缓存管理 │ ├─ RDEpubReaderPreloadController // 页面预加载、缓存管理
│ ├─ RDReaderSpreadResolver // 双页展开计算 │ ├─ RDEpubReaderSpreadResolver // 双页展开计算
│ └─ RDReaderTapRegionHandler // 点击区域分类(左/中/右) │ └─ RDEpubReaderTapRegionHandler // 点击区域分类(左/中/右)
├─ 数据源协议: ├─ 数据源协议:
│ ├─ RDReaderPageProvider (新) // 格式无关,优先级高 │ ├─ RDEpubReaderPageProvider (新) // 格式无关,优先级高
│ └─ RDReaderDataSource (旧) // 遗留兼容,通过 Adapter 适配 │ └─ RDEpubReaderDataSource (旧) // 遗留兼容,通过 Adapter 适配
└─ 手势流: └─ 手势流:
点击 → TapRegionHandler → 分类(左/中/右) 点击 → TapRegionHandler → 分类(左/中/右)
@@ -351,7 +351,7 @@ struct RDEPUBReaderConfiguration {
var numberOfColumns: Int // 1 2 var numberOfColumns: Int // 1 2
var columnGap: CGFloat // 20 var columnGap: CGFloat // 20
var theme: ReaderTheme // 6 var theme: ReaderTheme // 6
var displayType: RDReaderView.DisplayType // pageCurl/horizontal/vertical var displayType: RDEpubReaderView.DisplayType // pageCurl/horizontal/vertical
var onDemandChapterWindowSize: Int // 3 3 15 var onDemandChapterWindowSize: Int // 3 3 15
var metadataParsingConcurrency: Int // CPU var metadataParsingConcurrency: Int // CPU
var chapterWindowRadius: Int // var chapterWindowRadius: Int //
@@ -374,7 +374,7 @@ struct RDEPUBReaderConfiguration {
## 9. 目录结构 ## 9. 目录结构
``` ```
Sources/RDReaderView/ Sources/RDEpubReaderView/
├── EPUBCore/ # EPUB 解析与 WebView 渲染 ├── EPUBCore/ # EPUB 解析与 WebView 渲染
│ ├── Models/ # 数据模型 │ ├── Models/ # 数据模型
│ │ ├── RDEPUBAnnotationModels.swift │ │ ├── RDEPUBAnnotationModels.swift
@@ -428,19 +428,19 @@ Sources/RDReaderView/
│ ├── RDEPUBDTCoreTextRenderer.swift # DTCoreText 渲染器实现 │ ├── RDEPUBDTCoreTextRenderer.swift # DTCoreText 渲染器实现
│ ├── RDEPUBChapterData.swift # 章节查询门面 │ ├── RDEPUBChapterData.swift # 章节查询门面
│ ├── RDEPUBTextIndexTable.swift # 全书索引表 │ ├── RDEPUBTextIndexTable.swift # 全书索引表
│ └── RDPlainTextBookBuilder.swift # 纯文本 (.txt) 构建器 │ └── RDEpubPlainTextBookBuilder.swift # 纯文本 (.txt) 构建器
├── ReaderView/ # 通用翻页容器 ├── ReaderView/ # 通用翻页容器
│ ├── Paging/ # 翻页子系统 │ ├── Paging/ # 翻页子系统
│ │ ├── RDReaderPagingController.swift │ │ ├── RDEpubReaderPagingController.swift
│ │ ├── RDReaderPreloadController.swift │ │ ├── RDEpubReaderPreloadController.swift
│ │ ├── RDReaderSpreadResolver.swift │ │ ├── RDEpubReaderSpreadResolver.swift
│ │ └── RDReaderTapRegionHandler.swift │ │ └── RDEpubReaderTapRegionHandler.swift
│ ├── RDReaderView.swift # 主容器视图 │ ├── RDEpubReaderView.swift # 主容器视图
│ ├── RDReaderView+*.swift # 扩展(CollectionView/PageCurl/ToolView/ContentAccess │ ├── RDEpubReaderView+*.swift # 扩展(CollectionView/PageCurl/ToolView/ContentAccess
│ ├── RDReaderFlowLayout.swift # 自定义 CollectionView 布局 │ ├── RDEpubReaderFlowLayout.swift # 自定义 CollectionView 布局
│ ├── RDReaderContentCell.swift # 滚动模式 Cell │ ├── RDEpubReaderContentCell.swift # 滚动模式 Cell
│ └── RDReaderPageChildViewController.swift // 翻页模式子 VC │ └── RDEpubReaderPageChildViewController.swift // 翻页模式子 VC
└── EPUBUI/ # 阅读器 UI └── EPUBUI/ # 阅读器 UI
├── ReaderController/ # 控制器与协调器 ├── ReaderController/ # 控制器与协调器
@@ -494,7 +494,7 @@ Sources/RDReaderView/
| **Builder** | `RDEPUBBookPageMap.Builder` 增量构建页码映射 | | **Builder** | `RDEPUBBookPageMap.Builder` 增量构建页码映射 |
| **Strategy** | `RDEPUBTextRenderer` 协议,可替换渲染器实现 | | **Strategy** | `RDEPUBTextRenderer` 协议,可替换渲染器实现 |
| **Pipeline** | `RDEPUBTextTypesetterPipeline` 排版管线(8 个逻辑阶段,封装为 5-6 个顶层调用) | | **Pipeline** | `RDEPUBTextTypesetterPipeline` 排版管线(8 个逻辑阶段,封装为 5-6 个顶层调用) |
| **Adapter** | `RDReaderLegacyDataSourceAdapter` 适配旧数据源协议 | | **Adapter** | `RDEpubReaderLegacyDataSourceAdapter` 适配旧数据源协议 |
| **三级缓存** | 内存 → 磁盘摘要 → 全书分页,逐级降级 | | **三级缓存** | 内存 → 磁盘摘要 → 全书分页,逐级降级 |
| **Token 取消** | `paginationToken` 确保过期异步任务不干扰新任务 | | **Token 取消** | `paginationToken` 确保过期异步任务不干扰新任务 |
| **Frozen Parameters** | 后台任务冻结 `renderSignature`,避免运行中参数漂移 | | **Frozen Parameters** | 后台任务冻结 `renderSignature`,避免运行中参数漂移 |
+3 -3
View File
@@ -1,6 +1,6 @@
# ReadViewSDK 代码审查报告 # ReadViewSDK 代码审查报告
> 审查范围:`Sources/RDReaderView/` 全部 184 个 Swift 文件(约 32,300 行) > 审查范围:`Sources/RDEpubReaderView/` 全部 184 个 Swift 文件(约 32,300 行)
> 审查维度:并发安全、内存管理、API 正确性、Swift/iOS 平台特定、架构与依赖 > 审查维度:并发安全、内存管理、API 正确性、Swift/iOS 平台特定、架构与依赖
> 审查日期:2026-06-26 > 审查日期:2026-06-26
@@ -134,7 +134,7 @@ let stillChanged = abs(currentSize.width - self.lastTextPaginationPageSize!.widt
### H-10 · 依赖版本未锁定 ### H-10 · 依赖版本未锁定
`RDReaderView.podspec` 中 DTCoreText、SnapKit、SSAlertSwift 无版本约束: `RDEpubReaderView.podspec` 中 DTCoreText、SnapKit、SSAlertSwift 无版本约束:
```ruby ```ruby
s.dependency 'ZIPFoundation', '~> 0.9' # ✅ 已锁定 s.dependency 'ZIPFoundation', '~> 0.9' # ✅ 已锁定
@@ -143,7 +143,7 @@ s.dependency 'SnapKit' # ❌ 无约束
s.dependency 'SSAlertSwift' # ❌ 无约束 s.dependency 'SSAlertSwift' # ❌ 无约束
``` ```
**位置:** `RDReaderView.podspec:17-20` **位置:** `RDEpubReaderView.podspec:17-20`
--- ---
+4 -4
View File
@@ -298,14 +298,14 @@ return builder.build()
**触发条件:** `landscapeDualPageEnabled && isLandscape && !verticalScroll` **触发条件:** `landscapeDualPageEnabled && isLandscape && !verticalScroll`
**页面配对逻辑(RDReaderSpreadResolver):** **页面配对逻辑(RDEpubReaderSpreadResolver):**
- 有封面页:封面独占一屏,后续页面两两配对 - 有封面页:封面独占一屏,后续页面两两配对
- 配对规则:`(coverIndex, nil)`, `(1, 2)`, `(3, 4)`, ... - 配对规则:`(coverIndex, nil)`, `(1, 2)`, `(3, 4)`, ...
- 无封面页:标准偶奇配对 - 无封面页:标准偶奇配对
- 配对规则:`(0, 1)`, `(2, 3)`, `(4, 5)`, ... - 配对规则:`(0, 1)`, `(2, 3)`, `(4, 5)`, ...
**封面感知布局(RDReaderFlowLayout):** **封面感知布局(RDEpubReaderFlowLayout):**
``` ```
封面页: [====全屏宽度====] 封面页: [====全屏宽度====]
@@ -314,7 +314,7 @@ return builder.build()
### 5.3 页面预加载 ### 5.3 页面预加载
`RDReaderPreloadController` 管理两个缓存: `RDEpubReaderPreloadController` 管理两个缓存:
- `preloadedPageViews` - 预渲染的页面视图 - `preloadedPageViews` - 预渲染的页面视图
- `pageCurlCachedViews` - 当前显示的页面视图 - `pageCurlCachedViews` - 当前显示的页面视图
@@ -580,7 +580,7 @@ protocol RDEPUBReaderPersistence {
## 9. 纯文本 (.txt) 支持 ## 9. 纯文本 (.txt) 支持
`RDPlainTextBookBuilder` 复用 EPUB 渲染管线处理 .txt 文件: `RDEpubPlainTextBookBuilder` 复用 EPUB 渲染管线处理 .txt 文件:
1. **解码:** 依次尝试 UTF-8 → GB18030 → GBK 1. **解码:** 依次尝试 UTF-8 → GB18030 → GBK
2. **分章:** 正则匹配 `^(第[零一二三四五六七八九十百千万\d]+[章节回卷].*)$` 2. **分章:** 正则匹配 `^(第[零一二三四五六七八九十百千万\d]+[章节回卷].*)$`
+13 -13
View File
@@ -34,7 +34,7 @@ epubcfi(/6/4!ch01.xhtml/4/2/1:3)
顶层 CFI 模型,对应一个完整的 `epubcfi(...)` 字符串。 顶层 CFI 模型,对应一个完整的 `epubcfi(...)` 字符串。
**文件**`Sources/RDReaderView/EPUBCore/CFI/RDEPUBCFI.swift` **文件**`Sources/RDEpubReaderView/EPUBCore/CFI/RDEPUBCFI.swift`
```swift ```swift
public struct RDEPUBCFI: Codable, Equatable, Hashable { public struct RDEPUBCFI: Codable, Equatable, Hashable {
@@ -66,7 +66,7 @@ public enum RDEPUBCFISideBias: String, Codable {
路径模型,由一组 `RDEPUBCFIStep` 组成,描述从根节点到目标节点的遍历序列。 路径模型,由一组 `RDEPUBCFIStep` 组成,描述从根节点到目标节点的遍历序列。
**文件**`Sources/RDReaderView/EPUBCore/CFI/RDEPUBCFIPath.swift` **文件**`Sources/RDEpubReaderView/EPUBCore/CFI/RDEPUBCFIPath.swift`
```swift ```swift
public struct RDEPUBCFIPath: Codable, Equatable, Hashable { public struct RDEPUBCFIPath: Codable, Equatable, Hashable {
@@ -95,7 +95,7 @@ public struct RDEPUBCFIStep: Codable, Equatable, Hashable {
范围模型,表示文档中的一个连续区域,由父级 CFI 和起止 CFI 组成。 范围模型,表示文档中的一个连续区域,由父级 CFI 和起止 CFI 组成。
**文件**`Sources/RDReaderView/EPUBCore/CFI/RDEPUBCFIRange.swift` **文件**`Sources/RDEpubReaderView/EPUBCore/CFI/RDEPUBCFIRange.swift`
```swift ```swift
public struct RDEPUBCFIRange: Codable, Equatable, Hashable { public struct RDEPUBCFIRange: Codable, Equatable, Hashable {
@@ -116,7 +116,7 @@ public struct RDEPUBCFIRange: Codable, Equatable, Hashable {
将 CFI 字符串解析为结构化模型。 将 CFI 字符串解析为结构化模型。
**文件**`Sources/RDReaderView/EPUBCore/CFI/RDEPUBCFIParser.swift` **文件**`Sources/RDEpubReaderView/EPUBCore/CFI/RDEPUBCFIParser.swift`
```swift ```swift
public enum RDEPUBCFIParser { public enum RDEPUBCFIParser {
@@ -154,7 +154,7 @@ print(cfi.textAssertion?.exact) // Optional("目标文本")
将 CFI 模型序列化为标准字符串。 将 CFI 模型序列化为标准字符串。
**文件**`Sources/RDReaderView/EPUBCore/CFI/RDEPUBCFISerializer.swift` **文件**`Sources/RDEpubReaderView/EPUBCore/CFI/RDEPUBCFISerializer.swift`
```swift ```swift
public enum RDEPUBCFISerializer { public enum RDEPUBCFISerializer {
@@ -192,7 +192,7 @@ let serialized = RDEPUBCFISerializer.serialize(cfi)
将 CFI 解析为可直接用于资源定位的结果。 将 CFI 解析为可直接用于资源定位的结果。
**文件**`Sources/RDReaderView/EPUBCore/CFI/RDEPUBCFIResolver.swift` **文件**`Sources/RDEpubReaderView/EPUBCore/CFI/RDEPUBCFIResolver.swift`
```swift ```swift
public struct RDEPUBCFIResolverResult: Equatable { public struct RDEPUBCFIResolverResult: Equatable {
@@ -228,7 +228,7 @@ print(result.chapterOffset) // Optional(80)
从已知的章节信息生成 CFI。 从已知的章节信息生成 CFI。
**文件**`Sources/RDReaderView/EPUBCore/CFI/RDEPUBCFIGenerator.swift` **文件**`Sources/RDEpubReaderView/EPUBCore/CFI/RDEPUBCFIGenerator.swift`
```swift ```swift
public enum RDEPUBCFIGenerator { public enum RDEPUBCFIGenerator {
@@ -274,7 +274,7 @@ public enum RDEPUBCFIGenerator {
从 HTML 源码中提取带有 `id` 属性的元素路径映射。 从 HTML 源码中提取带有 `id` 属性的元素路径映射。
**文件**`Sources/RDReaderView/EPUBCore/CFI/RDEPUBCFIDOMPathBuilder.swift` **文件**`Sources/RDEpubReaderView/EPUBCore/CFI/RDEPUBCFIDOMPathBuilder.swift`
```swift ```swift
public enum RDEPUBCFIDOMPathBuilder { public enum RDEPUBCFIDOMPathBuilder {
@@ -296,7 +296,7 @@ public enum RDEPUBCFIDOMPathBuilder {
当 CFI 精确定位失败时(如 DOM 结构变更),恢复引擎通过多级降级策略尝试找到最佳匹配位置。 当 CFI 精确定位失败时(如 DOM 结构变更),恢复引擎通过多级降级策略尝试找到最佳匹配位置。
**文件**`Sources/RDReaderView/EPUBCore/CFI/RDEPUBCFIRecoveryEngine.swift` **文件**`Sources/RDEpubReaderView/EPUBCore/CFI/RDEPUBCFIRecoveryEngine.swift`
### 5.1 恢复置信度 ### 5.1 恢复置信度
@@ -361,7 +361,7 @@ let result = RDEPUBCFIRecoveryEngine.recover(
文本断言用于在 CFI 定位后验证所指位置的文本内容是否符合预期,增强定位鲁棒性。 文本断言用于在 CFI 定位后验证所指位置的文本内容是否符合预期,增强定位鲁棒性。
**文件**`Sources/RDReaderView/EPUBCore/CFI/RDEPUBCFITextAssertion.swift` **文件**`Sources/RDEpubReaderView/EPUBCore/CFI/RDEPUBCFITextAssertion.swift`
```swift ```swift
public struct RDEPUBCFITextAssertion: Codable, Equatable, Hashable { public struct RDEPUBCFITextAssertion: Codable, Equatable, Hashable {
@@ -393,7 +393,7 @@ print(assertion?.suffix) // Optional("后续内容")
提供宽松解析接口,兼容非标准 CFI 格式。 提供宽松解析接口,兼容非标准 CFI 格式。
**文件**`Sources/RDReaderView/EPUBCore/CFI/RDEPUBCFICompatibility.swift` **文件**`Sources/RDEpubReaderView/EPUBCore/CFI/RDEPUBCFICompatibility.swift`
```swift ```swift
public enum RDEPUBCFICompatibility { public enum RDEPUBCFICompatibility {
@@ -418,7 +418,7 @@ public enum RDEPUBCFICompatibility {
CFI 映射是章节级别的索引结构,将 CFI 路径映射到章节文本偏移量,是容错恢复引擎的核心数据源。 CFI 映射是章节级别的索引结构,将 CFI 路径映射到章节文本偏移量,是容错恢复引擎的核心数据源。
**文件**`Sources/RDReaderView/EPUBCore/CFI/RDEPUBCFIMap.swift` **文件**`Sources/RDEpubReaderView/EPUBCore/CFI/RDEPUBCFIMap.swift`
### 8.1 RDEPUBCFIMap ### 8.1 RDEPUBCFIMap
@@ -506,7 +506,7 @@ public struct RDEPUBCFITokenAnchor: Codable, Equatable {
## 9. 错误类型(RDEPUBCFIError ## 9. 错误类型(RDEPUBCFIError
**文件**`Sources/RDReaderView/EPUBCore/CFI/RDEPUBCFIError.swift` **文件**`Sources/RDEpubReaderView/EPUBCore/CFI/RDEPUBCFIError.swift`
```swift ```swift
public enum RDEPUBCFIError: Error, Equatable { public enum RDEPUBCFIError: Error, Equatable {
+2 -2
View File
@@ -16,8 +16,8 @@
- 渐进补全:后台逐步补全所有章节的页码信息 - 渐进补全:后台逐步补全所有章节的页码信息
**关键文件** **关键文件**
- `Sources/RDReaderView/EPUBUI/ReaderController/ChapterRuntime/` 目录 - `Sources/RDEpubReaderView/EPUBUI/ReaderController/ChapterRuntime/` 目录
- `Sources/RDReaderView/EPUBUI/ReaderController/RDEPUBReaderPaginationCoordinator.swift` - `Sources/RDEpubReaderView/EPUBUI/ReaderController/RDEPUBReaderPaginationCoordinator.swift`
--- ---
+9 -9
View File
@@ -105,15 +105,15 @@
## Evidence(仓库路径) ## Evidence(仓库路径)
- Build flags`Podfile` - Build flags`Podfile`
- Pod metadata/toolchain`RDReaderView.podspec` - Pod metadata/toolchain`RDEpubReaderView.podspec`
- Archive extraction`Sources/RDReaderView/EPUBCore/RDEPUBParser+Archive.swift` - Archive extraction`Sources/RDEpubReaderView/EPUBCore/RDEPUBParser+Archive.swift`
- Resource path validationpost-extraction):`Sources/RDReaderView/EPUBCore/RDEPUBParser+Resources.swift` - Resource path validationpost-extraction):`Sources/RDEpubReaderView/EPUBCore/RDEPUBParser+Resources.swift`
- Scheme handler reads full file bytes`Sources/RDReaderView/EPUBCore/RDEPUBResourceURLSchemeHandler.swift` - Scheme handler reads full file bytes`Sources/RDEpubReaderView/EPUBCore/RDEPUBResourceURLSchemeHandler.swift`
- WebView bridge + message handling`Sources/RDReaderView/EPUBCore/RDEPUBWebView+JavaScriptBridge.swift` - WebView bridge + message handling`Sources/RDEpubReaderView/EPUBCore/RDEPUBWebView+JavaScriptBridge.swift`
- External link opening`Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift` - External link opening`Sources/RDEpubReaderView/EPUBUI/RDEPUBReaderController.swift`
- UserDefaults persistence implementation`Sources/RDReaderView/EPUBUI/RDEPUBReaderPersistence.swift` - UserDefaults persistence implementation`Sources/RDEpubReaderView/EPUBUI/RDEPUBReaderPersistence.swift`
- Hidden paginator web view`Sources/RDReaderView/EPUBCore/RDEPUBPaginator.swift` - Hidden paginator web view`Sources/RDEpubReaderView/EPUBCore/RDEPUBPaginator.swift`
- Debug logging`Sources/RDReaderView/EPUBCore/RDEPUBWebViewDebug.swift` - Debug logging`Sources/RDEpubReaderView/EPUBCore/RDEPUBWebViewDebug.swift`
- Vendored dependencies`Pods/``ReadViewDemo/Pods/` - Vendored dependencies`Pods/``ReadViewDemo/Pods/`
--- ---
+23 -23
View File
@@ -6,43 +6,43 @@
## 语言与工程约束 ## 语言与工程约束
- **主要语言**SwiftPodspec 声明 `s.swift_versions = ["5.10"]`,见 `RDReaderView.podspec` - **主要语言**SwiftPodspec 声明 `s.swift_versions = ["5.10"]`,见 `RDEpubReaderView.podspec`
- **最低系统版本**Podspec `iOS 15.0``RDReaderView.podspec`),示例工程 Podfile/构建设置里常见为 `iOS 15.6``Podfile` - **最低系统版本**Podspec `iOS 15.0``RDEpubReaderView.podspec`),示例工程 Podfile/构建设置里常见为 `iOS 15.6``ReadViewDemo/Podfile`
- **依赖管理**CocoaPods`Podfile``ReadViewDemo/Podfile``Podfile.lock` - **依赖管理**CocoaPods`ReadViewDemo/Podfile``ReadViewDemo/Podfile.lock`
## 命名约定 ## 命名约定
**文件/类型命名(Swift):** **文件/类型命名(Swift):**
- 以类型名为文件名的单文件组织较常见:`Sources/RDReaderView/EPUBCore/RDEPUBParser.swift` - 以类型名为文件名的单文件组织较常见:`Sources/RDEpubReaderView/EPUBCore/RDEPUBParser.swift`
- 大量使用前缀区分模块域: - 大量使用前缀区分模块域:
- `RD...`:阅读器 UI/控制器相关(如 `Sources/RDReaderView/RDReaderView.swift``Sources/RDReaderView/RDURLReaderController.swift` - `RD...`:阅读器 UI/控制器相关(如 `Sources/RDEpubReaderView/RDEpubReaderView.swift``Sources/RDEpubReaderView/RDEpubURLReaderController.swift`
- `RDEPUB...`EPUB Core/UI/渲染相关(如 `Sources/RDReaderView/EPUBCore/RDEPUBModels.swift` - `RDEPUB...`EPUB Core/UI/渲染相关(如 `Sources/RDEpubReaderView/EPUBCore/RDEPUBModels.swift`
- Extension 文件使用 `+` 命名:`Sources/RDReaderView/EPUBUI/RDEPUBReaderController+ContentDelegates.swift` - Extension 文件使用 `+` 命名:`Sources/RDEpubReaderView/EPUBUI/RDEPUBReaderController+ContentDelegates.swift`
**变量/函数命名:** **变量/函数命名:**
- 基本遵循 Swift lowerCamelCase`parse(epubURL:)``Sources/RDReaderView/EPUBCore/RDEPUBParser.swift` - 基本遵循 Swift lowerCamelCase`parse(epubURL:)``Sources/RDEpubReaderView/EPUBCore/RDEPUBParser.swift`
- 常量多用 `static let``kRDEPUBHighlightAttributeName``Sources/RDReaderView/EPUBCore/Models/RDEPUBAnnotationModels.swift` - 常量多用 `static let``kRDEPUBHighlightAttributeName``Sources/RDEpubReaderView/EPUBCore/Models/RDEPUBAnnotationModels.swift`
## 代码风格与排版(从现有代码归纳) ## 代码风格与排版(从现有代码归纳)
**缩进与换行:** **缩进与换行:**
- 多数文件使用 4 空格缩进(示例:`ReadViewDemo/ReadViewDemo/ViewController.swift``Sources/RDReaderView/EPUBCore/RDEPUBModels.swift` - 多数文件使用 4 空格缩进(示例:`ReadViewDemo/ReadViewDemo/ViewController.swift``Sources/RDEpubReaderView/EPUBCore/RDEPUBModels.swift`
- 历史代码中更常见”强制换行/多行括号”风格 - 历史代码中更常见”强制换行/多行括号”风格
**空行与分组:** **空行与分组:**
- UI 相关文件常用空行分隔属性/初始化/布局段落 - UI 相关文件常用空行分隔属性/初始化/布局段落
- `// MARK:` 用于分区组织(示例:`Sources/RDReaderView/RDReaderGestureController.swift``Sources/RDReaderView/EPUBTextRendering/RDPlainTextBookBuilder.swift` - `// MARK:` 用于分区组织(示例:`Sources/RDEpubReaderView/RDEpubReaderGestureController.swift``Sources/RDEpubReaderView/EPUBTextRendering/RDEpubPlainTextBookBuilder.swift`
**类型组织:** **类型组织:**
- 偏好用 `extension` 拆分职责/协议实现(示例:`ReadViewDemo/ReadViewDemo/ViewController.swift``UITableViewDataSource/Delegate` - 偏好用 `extension` 拆分职责/协议实现(示例:`ReadViewDemo/ReadViewDemo/ViewController.swift``UITableViewDataSource/Delegate`
- API 暴露处使用 `public``public final class``public enum/struct`(示例:`Sources/RDReaderView/EPUBCore/RDEPUBModels.swift` - API 暴露处使用 `public``public final class``public enum/struct`(示例:`Sources/RDEpubReaderView/EPUBCore/RDEPUBModels.swift`
- “对外只读、内部可写”常用 `public internal(set)`(示例:`Sources/RDReaderView/EPUBCore/RDEPUBParser.swift` - “对外只读、内部可写”常用 `public internal(set)`(示例:`Sources/RDEpubReaderView/EPUBCore/RDEPUBParser.swift`
## 导入与依赖使用 ## 导入与依赖使用
**import** **import**
- UIKit/UI 文件:`import UIKit`(大量文件) - UIKit/UI 文件:`import UIKit`(大量文件)
- Core/模型文件:`import Foundation`(如 `Sources/RDReaderView/EPUBCore/RDEPUBModels.swift` - Core/模型文件:`import Foundation`(如 `Sources/RDEpubReaderView/EPUBCore/RDEPUBModels.swift`
- 三方依赖按需引入: - 三方依赖按需引入:
- `SnapKit`:布局 - `SnapKit`:布局
- `SSAlertSwift`:弹窗/提示 - `SSAlertSwift`:弹窗/提示
@@ -52,15 +52,15 @@
## 错误处理与日志 ## 错误处理与日志
- Core 解析层倾向用 `throws` + 自定义 `Error`(示例:`Sources/RDReaderView/EPUBCore/RDEPUBParser.swift``Sources/RDReaderView/EPUBCore/RDEPUBModels.swift``RDEPUBParserError` - Core 解析层倾向用 `throws` + 自定义 `Error`(示例:`Sources/RDEpubReaderView/EPUBCore/RDEPUBParser.swift``Sources/RDEpubReaderView/EPUBCore/RDEPUBModels.swift``RDEPUBParserError`
- UI/控制器层常见 `guard` 早返回(示例:`ReadViewDemo/ReadViewDemo/ViewController.swift` - UI/控制器层常见 `guard` 早返回(示例:`ReadViewDemo/ReadViewDemo/ViewController.swift`
- 未检测到统一日志框架(未发现专用 logging package/config);出现时以系统 API/局部输出为主(需按具体文件核对)。 - 未检测到统一日志框架(未发现专用 logging package/config);出现时以系统 API/局部输出为主(需按具体文件核对)。
## 注释与文档 ## 注释与文档
- **项目规则(强约束)**:代码标识符保持英文,但**代码注释/文档/提交信息使用中文**(见 `CONTEXT.md`)。 - **项目规则(强约束)**:代码标识符保持英文,但**代码注释/文档/提交信息使用中文**(见 `CONTEXT.md`)。
- 历史文件常带 Xcode 头部注释块(示例:`Sources/RDReaderView/ReaderView/RDReaderView.swift`)。 - 历史文件常带 Xcode 头部注释块(示例:`Sources/RDEpubReaderView/ReaderView/RDEpubReaderView.swift`)。
- 公共 API 处存在少量三斜线文档注释(示例:`Sources/RDReaderView/RDReaderView.swift` 的中文说明)。 - 公共 API 处存在少量三斜线文档注释(示例:`Sources/RDEpubReaderView/RDEpubReaderView.swift` 的中文说明)。
## Lint / Formatter / 静态检查 ## Lint / Formatter / 静态检查
@@ -76,10 +76,10 @@
## Evidence(关键证据文件) ## Evidence(关键证据文件)
- `CONTEXT.md` - `CONTEXT.md`
- `Podfile` - `ReadViewDemo/Podfile`
- `RDReaderView.podspec` - `RDEpubReaderView.podspec`
- `ReadViewDemo/ReadViewDemo/ViewController.swift` - `ReadViewDemo/ReadViewDemo/ViewController.swift`
- `Sources/RDReaderView/ReaderView/RDReaderView.swift` - `Sources/RDEpubReaderView/ReaderView/RDEpubReaderView.swift`
- `Sources/RDReaderView/EPUBCore/RDEPUBParser.swift` - `Sources/RDEpubReaderView/EPUBCore/RDEPUBParser.swift`
- `Sources/RDReaderView/EPUBCore/RDEPUBModels.swift` - `Sources/RDEpubReaderView/EPUBCore/RDEPUBModels.swift`
- `Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift` - `Sources/RDEpubReaderView/EPUBUI/RDEPUBReaderController.swift`
+1 -1
View File
@@ -471,7 +471,7 @@ final class RDEPUBTextSearchEngine: RDEPUBSearchEngine {
## 9. 纯文本构建器 ## 9. 纯文本构建器
**文件:** `RDPlainTextBookBuilder.swift` **文件:** `RDEpubPlainTextBookBuilder.swift`
从纯文本(.txt)文件构建 `RDEPUBTextBook`,用于支持 TXT 格式阅读。 从纯文本(.txt)文件构建 `RDEPUBTextBook`,用于支持 TXT 格式阅读。
+8 -8
View File
@@ -48,7 +48,7 @@ public final class RDEPUBReaderController: UIViewController {
let epubURL: URL // EPUB let epubURL: URL // EPUB
let persistence: RDEPUBReaderPersistence? // let persistence: RDEPUBReaderPersistence? //
let dependencies: RDEPUBReaderDependencies // let dependencies: RDEPUBReaderDependencies //
let readerView = RDReaderView() // let readerView = RDEpubReaderView() //
} }
``` ```
@@ -57,7 +57,7 @@ public final class RDEPUBReaderController: UIViewController {
| 文件 | 职责 | | 文件 | 职责 |
|------|------| |------|------|
| `RDEPUBReaderController+PublicAPI.swift` | 公开 API(跳转、搜索、标注、书签) | | `RDEPUBReaderController+PublicAPI.swift` | 公开 API(跳转、搜索、标注、书签) |
| `RDEPUBReaderController+DataSource.swift` | `RDReaderPageProvider` 实现 | | `RDEPUBReaderController+DataSource.swift` | `RDEpubReaderPageProvider` 实现 |
| `RDEPUBReaderController+ContentDelegates.swift` | WebView/TextContentView 代理路由 | | `RDEPUBReaderController+ContentDelegates.swift` | WebView/TextContentView 代理路由 |
| `RDEPUBReaderController+LocationResolution.swift` | 页码/位置解析、阅读状态同步 | | `RDEPUBReaderController+LocationResolution.swift` | 页码/位置解析、阅读状态同步 |
| `RDEPUBReaderController+ExternalLinks.swift` | 外部链接处理(白名单、确认弹窗) | | `RDEPUBReaderController+ExternalLinks.swift` | 外部链接处理(白名单、确认弹窗) |
@@ -120,7 +120,7 @@ public protocol RDEPUBReaderPersistence: AnyObject {
```swift ```swift
final class RDEPUBReaderContext { final class RDEPUBReaderContext {
weak var controller: RDEPUBReaderController? weak var controller: RDEPUBReaderController?
weak var readerView: RDReaderView? weak var readerView: RDEpubReaderView?
var dependencies: RDEPUBReaderDependencies var dependencies: RDEPUBReaderDependencies
var runtime: RDEPUBReaderRuntime? var runtime: RDEPUBReaderRuntime?
@@ -155,7 +155,7 @@ public struct RDEPUBReaderDependencies {
public var makeParser: () -> RDEPUBParser public var makeParser: () -> RDEPUBParser
public var makePaginator: () -> RDEPUBPaginator public var makePaginator: () -> RDEPUBPaginator
public var makeTextBookBuilder: (RDEPUBTextRenderer, RDEPUBTextBookCache?, RDEPUBTextLayoutConfig) -> RDEPUBTextBookBuilder public var makeTextBookBuilder: (RDEPUBTextRenderer, RDEPUBTextBookCache?, RDEPUBTextLayoutConfig) -> RDEPUBTextBookBuilder
public var makePlainTextBookBuilder: (RDEPUBTextRenderer, RDEPUBTextLayoutConfig) -> RDPlainTextBookBuilder public var makePlainTextBookBuilder: (RDEPUBTextRenderer, RDEPUBTextLayoutConfig) -> RDEpubPlainTextBookBuilder
public var makeTextRenderer: (RDEPUBTextRenderingEngine) -> RDEPUBTextRenderer public var makeTextRenderer: (RDEPUBTextRenderingEngine) -> RDEPUBTextRenderer
public static var live: RDEPUBReaderDependencies // public static var live: RDEPUBReaderDependencies //
@@ -659,9 +659,9 @@ struct RDEPUBReaderUIState {
页图协调器(按需分页与全量分页的结果合并)。 页图协调器(按需分页与全量分页的结果合并)。
### 10.6 RDURLReaderController ### 10.6 RDEpubURLReaderController
**文件:** `RDURLReaderController.swift` **文件:** `RDEpubURLReaderController.swift`
URL 阅读器控制器(用于打开单个 URL)。 URL 阅读器控制器(用于打开单个 URL)。
@@ -715,12 +715,12 @@ RDEPUBReaderRuntime.applyParsedPublication()
├── RDEPUBBookPageMap 生成 ├── RDEPUBBookPageMap 生成
└── RDReaderView.transitionToPage() → 显示页面 └── RDEpubReaderView.transitionToPage() → 显示页面
用户翻页 用户翻页
RDReaderView.currentPage 变化 RDEpubReaderView.currentPage 变化
├── RDEPUBReaderLocationCoordinator.recordPageChangeIfNeeded() ├── RDEPUBReaderLocationCoordinator.recordPageChangeIfNeeded()
│ └── persistence.saveLocation() │ └── persistence.saveLocation()
@@ -0,0 +1,154 @@
# 长章节内存优化 — 代码实施清单
> 背景:DTCoreText 文本页已采用"整章上下文布局",分页与显示边界一致(不可回退)。现状代价:每次翻页/预加载都整章重拷贝 + 重排版后丢弃,且章节缓存里每页常驻一份子串副本。
>
> 总原则:保留"整章参与排版",去掉"整章按页复制、按页可变、按页缓存"。不改分页器,不上窗口化。
---
## P0:立即可做
### P0-1 削减每页 `content` 子串常驻副本
**现状**:分页产物为每页保存整段 `attributedSubstring`,全章加总约等于又一份整章副本,随邻接窗口(radius 1,共 3 章)常驻。
生成点:
- `Sources/RDEpubReaderView/EPUBUI/ReaderController/ChapterRuntime/RDEPUBChapterLoader.swift:494``buildPagesFromRanges`
- `Sources/RDEpubReaderView/EPUBTextRendering/BuildPipeline/RDEPUBTextBookBuilder.swift:294`
- `Sources/RDEpubReaderView/EPUBTextRendering/RDEpubPlainTextBookBuilder.swift:78`
现存全部消费点(已盘点确认,仅 3 处):
- `RDEPUBTextContentView.swift:562` — 封面检测 `coverImage(from: page.content)`,仅 `pageIndexInChapter == 0` 且 href 含 "cover"
- `RDEPUBTextContentView.swift:641``normalizedPageContent`,仅非 DTCoreText 的 `#else` 回退路径调用
- `RDEPUBTextBookBuilder.swift:93` — 构建期 debug 日志 preview
**任务**
- [x]`RDEPUBTextPage.content` 改为按需构造:已改为基于 `chapterContent` + `contentRange` 的计算属性(带范围钳制),公开读取 API 不变
- [x] 三个构造点(`RDEPUBChapterLoader` / `RDEPUBTextBookBuilder` / `RDEpubPlainTextBookBuilder`)不再生成每页子串;消费点经计算属性透明按需构造
- [x] `Equatable` 确认:`content` 为派生值,由 `contentRange` + `chapterContent` 判等覆盖,语义不变
**API 兼容性注意**
- `RDEPUBTextPage` 当前是 `public struct``content` 也是公开存储属性;如果 SDK 已存在外部接入方直接读取 `page.content`,则不应直接移除该字段
- 优先方案是保留 `content` 对外访问语义,但将其改为按需构造的计算属性或受控访问器,避免对外 API 断裂
- 若确认当前版本尚无稳定外部依赖,才可以评估把 `content` 从存储属性调整为内部实现细节
- 在实施前应补做一次全仓库与接入侧检索,确认是否有外部调用依赖 `page.content` 的“可变存储”特性
**验收**:打开 3 章窗口后常驻内存下降约"一整章文本规模 × 窗口章数";封面页显示正常;非 DTCoreText 回退路径回归通过;`--demo-pagination-validate` 命中数不回升。
### P0-2 增加内存打点
- [x] 已新增 `RDEPUBMemoryProbe``--demo-memory-probe` 启动参数开启,输出 phys_footprint);挂载点:`RDEPUBChapterRuntimeStore.insertChapter``pageNum` 回调每 20 次翻页、`invalidateAllForSettingsChange``viewWillTransition` 转屏完成
- [x]`--demo-memory-probe` 运行 Demo 即可采集对比基线
### P0-3 保持现有缓存限制,防止错误复用
- [x] `shouldAvoidReaderPageCaching``RDEPUBTextContentView.swift:319`)保持现状,已补注释说明放开的前置条件是 P1-1 落地
- [x] 本阶段未改 `RDEpubReaderPreloadController` 行为
---
## P1:第二波(P1-1 与 P1-2 必须同批落地)
### P1-1 章节级共享 displayContent
**现状**`RDEPUBTextContentView.configure` 每次创建整章可变副本(`RDEPUBTextContentView.swift:443`)并按页写入三类属性:页范围主题色(453)、页范围暗黑图附件替换(448)、按页裁剪的高亮属性(459 `applyHighlightsToContent`)。
**约束**:共享字符串一旦被任一页的 `DTCoreTextLayouter`/framesetter 持有,就不得再修改——所有属性必须在共享内容构建时一次性注入,或转为 overlay(见 P1-2)。
**任务**
- [x] 已新建 `RDEPUBChapterDisplayContentCache`LRU 容量 2,主线程限定,`RDEPUBReaderController` 持有):`content` + `layouter` + `signature`
- [x] 构建共享内容时一次性注入:全章范围主题色;高亮/下划线因 P1-2 转 overlay,不进共享内容也不进签名
- [x] 暗黑图策略选 a:构建时全章一次性替换,复用 `RDEPUBDarkImageAdjuster` 的 NSCache
- [x] `RDEPUBTextContentView.configure` 已改为接收 `displayCache` 参数并引用共享 entry,整章拷贝已删除
- [x] 签名 = 章节内容对象标识 + 长度 + 主题双色 + 暗黑图配置;设置/主题变化经签名自动失效重建(高亮变化不触发重建,仅重建 overlay 装饰)
- [x] 控制器在 `didReceiveMemoryWarning` 清空 display cache;设置变更重建 chapterContent 实例后由签名兜底换代
**验收**:连续翻页不再出现整章拷贝(打点确认);翻页延迟下降;高亮显示、高亮点击菜单、underline、搜索 currentMatch、附件点击、选区拖拽全部回归通过。
> 2026-07-08 回归记录:高亮/批注/选区/翻页/设置 13 项 UI 用例全过;SearchTests 10 项失败,但已在会话前提交 5a41066 与 P0 提交 e4e629a 上复现同样失败(搜索 0 命中),确认为先于本优化存在的独立回归(最后一次已知通过是 2026-06-08 全量跑),需单独排查,与 P1 改动无关。
### P1-2 高亮/下划线剥离为 overlay(与 P1-1 绑定)
**现状**:搜索高亮已走 overlay`RDEPUBTextContentView.swift:759``buildDecorations``highlights: []`,注释明确为避免双画);普通高亮/下划线仍靠写 `kRDEPUBHighlightAttributeName` / `kRDEPUBUnderlineAttributeName` 属性由 render view 绘制。
**任务**
- [x] `buildDecorations` 已传入真实 highlights,高亮进背景层(文字下方)、下划线进前景层
- [x] `applyHighlightsToContent` 与 render view 的 `drawHighlights`/`computeHighlightRects`/`attributedDisplayContent` 已整体移除,无双画
- [x] 高亮命中与菜单锚点继续走 `interactionController.selectionRects`,坐标保持 chapter-absolute(未改动)
**验收**:高亮/下划线视觉与改前一致(含跨页高亮的页内裁剪);高亮点击弹菜单正常;与搜索高亮叠加时无双画。
### P1-3 章节级共享 display layouter
**现状**:每个页面视图各建一个 `DTCoreTextLayouter``RDEPUBTextContentView.swift:465`);章节运行时已有分页用 `RDEPUBTextLayouter``RDEPUBRuntimeChapter.layouter`),display layouter 向同一模式靠拢。
**任务**
- [x] `DTCoreTextLayouter` 已随共享内容放入 display cache,与 signature 同生命周期
- [x] 页面按 `bounds` + `page.contentRange``layoutFrame``shouldCacheLayoutFrames = false` 保持,layoutFrame 按页新建(VerticalJustifier 的修改不会污染共享对象)
- [x] framesetter 只依赖字符串、与 bounds 无关,bounds 变化无需失效共享对象;layoutFrame 随页面视图释放
- [x] `RDEPUBTextPageBoundaryValidator` 校验路径不变
**验收**:翻页时不再重建 framesetter(打点确认);横竖屏切换后布局正确;`--demo-pagination-validate` 不回升。
### P1-4 评估重新放开文本页预加载缓存
前置:P1-1~P1-3 全部落地后,页面视图不再持有整章副本。
- [ ] 调整 `shouldAvoidReaderPageCaching` 判定,允许轻量化后的文本页进入 `RDEpubReaderPreloadController` 缓存
- [ ] 对比放开前后的翻页流畅度与峰值内存,数据不佳则回退此项
---
## P2:视 P0/P1 打点数据决定
### P2-1 章节级 LRU 分层淘汰
**现状**`RDEPUBChapterRuntimeStore` 已有邻接窗口(`windowSpineIndices`radius 1)、`evictableSpineIndices` / `evict` / `evictAllExceptCurrent` / `handleMemoryWarning` / `invalidateAllForSettingsChange`;磁盘侧有 `RDEPUBChapterSummaryDiskCache`(分页结果 + cfiMap + metadata)。当前邻接章节仍持有完整 `RDEPUBRuntimeChapter`typesetString + pages + layouter)。
**任务**
- [ ] 邻接章节降级:保留 page map / chapter summary / 分页结果,释放 `typesetAttributedString`、display content、layouter
- [ ] 进入邻接章节时按 summary 重建,测量重建延迟;延迟不可接受则维持现状
- [ ] 远距章节仅保留磁盘缓存 + 轻量 metadata(现有 evict 已覆盖,确认即可)
### P2-2 图片附件深化
**现状**:暗黑图已走 `NSCache`50 MB / 100 张,`RDEPUBDarkImageAdjuster.swift:9`)且仅处理当前页范围;章节运行时另有 100 MB `imageCache`
**任务**
- [ ] 大图按显示尺寸下采样后再参与 attachment
- [ ] 页面离屏后释放 attachment 强引用的已解码大图;避免共享 content 长期持有
- [ ] 回归:图片点击查看、暗黑模式切换、附件位置与命中
---
## P3:仅在 P0–P2 之后数据仍不达标时
- [ ] 上下文窗口化布局(当前页所在段落块 + 前后足以影响断行的文本窗口)。风险最高:易重新引入分页/显示边界不一致(CTTypesetter 断行上下文敏感,为已知已解 bug 的根因),选区/搜索/高亮/附件索引需全部重验。默认不做。
---
## 明确不做
1. 回退"页内子串重新布局"(重新引入显示不全/页末孤字)
2. 取消 chapter-absolute 索引(破坏点击/选区/高亮定位一致性)
3. 先改分页器(正确性与性能问题混杂,无法回归)
4. 未落地 P1 前放开文本页 reader 级缓存(会驻留多份整章副本)
---
## 各阶段统一验收指标
**正确性**`--demo-pagination-validate` 命中数不回升;长章节页末无孤字/缺字;高亮显示、搜索高亮、附件点击、选区拖拽行为一致。
**内存**(对比 P0-2 基线):打开长章节后常驻;连续翻页 20 页峰值;设置变更前后峰值;横竖屏切换峰值。
**性能**:首次打开章节耗时;连续翻页帧稳定性;设置切换恢复时间。
+123
View File
@@ -0,0 +1,123 @@
# 分页问题调查记录(2026-07-06
调查载体:《宝山辽墓材料与释读》(textReflowable / DTCoreText 路径),iPhone 15 Pro Max 尺寸(430×932,内容区 398×815pt)。当日共调查两个独立问题:
---
## 问题一:页底留空一行多,下一页首行未上移(已修复)
### 现象
第 6 页底部留有约 1.7 行高的空白,第 7 页首行"人目为帝羓,信有之也。[注]"本可容纳在第 6 页。截图实测:行距 75px@3x(25pt),第 6 页底部空隙 138px,足够再排一行。
### 根因链
1. `RDEPUBSemanticMarkerInjector.inferredHints` 给**所有** `<img>` 打上 `avoidPageBreakInside` 提示——包括行内脚注小图标(`<img class="qqreader-footnote">`,即"注"字图标)。
2. `RDEPUBPageBreakPolicy.shouldTreatAvoidHintAsBlockProtection` 本有豁免:`blockKind == .attachment && placement != .centered → 不保护`。但 `applyPaginationSemantics` 按**结束标记的字符串顺序**应用属性:`<img>` 的结束标记先于外层 `<p>`,段落随后把图标位置的 `.rdPageBlockKind``attachment` 覆盖为 `paragraph`;而 `hints``placement` 因段落不携带这两个属性而幸存。豁免条件因此失效。
3. `trimmedRangeForAvoidPageBreakInside` 把含注图标的行当作"不可分页块"的行,从页底裁掉(最多 3 行),推到下一页,留下空白。
### 验证手段
- Demo 启动参数 `--demo-pagination-debug` 输出 `[PAGINATION-DEBUG] avoidPageBreakInside removed N lines: …`;修复前被裁的行**全部**含 ``(脚注图标)。
- 复现命令:`--demo-book-title 宝山 --demo-page 6 --demo-reset-state --demo-clear-cache --demo-pagination-debug`
### 修复
`RDEPUBPageBreakPolicy.swift`:豁免条件放宽为 `(blockKind == .attachment || placement != nil) && placement != .centered`——不再依赖易被覆盖的 blockKind,只要附件 placement 是行内/基线(非居中)就不锁行。居中插图(bodyPic)的整块保护不受影响;同时覆盖 `s-pic`/`h-pic`/`g-pic` 等生僻字行内小图。
修复后验证:含 `` 的裁剪从多处降为 0;"人目为帝羓"行回到第 6 页且下一段首行补齐;全书页数 280 → 273(消除欠填页)。
> 备选方案(未采用):修 `applyPaginationSemantics` 的嵌套覆盖顺序(内层优先)。会改变列表/表格的 blockRange 语义,风险大。
---
## 问题二:页范围与显示内容度量错配,行中断页(2026-07-07 已定位根因,见文末新增章节)
### 现象
第 10 页最后一行只有孤字"相",第 11 页从"对简化,且无构造细致的翼墙。"开始——断点落在句子中间而非行边界。
### 铁证推理
当前设置下满行 26 字(字号 15、内容宽 398pt)。第 10 页范围要在"…而2号墓的门楼则相"后断开,要求该行装下 **27 字**——当前排版下不可能。**结论:页范围产生于另一套度量(更小字号或更宽版面),与显示时的排版不一致。** 同一套排版中分页断点必然落在行边界,孤字不可能出现。
### 已排除(受控实验)
`RDEPUBChapterLoader` 临时加 `--demo-chapter-dump` 转储(typesetString 逐属性段 + 页范围,写入 app Documents,实验后已还原):
- 冷启动(`MISS(fullRender)` 全量渲染分页)与热启动(`HIT(diskSummary)` 复用磁盘页范围)两条路径的字符串与页范围**完全一致**。字体压平、双重 `normalizeReadingAttributes`、语言码缺失(`makeChapterRenderRequest` 未传 `contentLanguageCode`)、`TailNormalizer` 尾页合并均验证为无影响或幂等。
- 持久缓存键控正确:`RDEPUBChapterCacheKey.renderSignature` 含字体名/字号/行距倍数/lineSpacing/`layoutConfig.cacheSignature`(宽高、四边距、分栏、hyphenation、schemaVersion=17+ 章节内容哈希。同设置跨启动复用安全。
### 主要嫌疑:会话中改设置的换表过渡态
- 用户两组截图之间调过行距(行距 25pt/页 32 行 → 28pt/页 28 行,字号未变);出现问题时显示总页数 196,既非 1.6 档全量分页(~273)也非 1.8 档(~290)——当时显示的是**未收敛的中间页表**。
- 代码窗口:
- `RDEPUBReaderRuntime.scheduleSettingsPreviewRepagination`:设置面板打开期间只重排**当前章**并 `applySettingsPreviewPageMap` 换部分页表,全量重排推迟到面板关闭(`needsFullRepaginationAfterSettingsClose`)。
- `RDEPUBChapterRuntimeStore.chapterDataCache` 仅按 spineIndex 键控(无样式维度);`invalidateAllForSettingsChange` 清空后,**变更前已在飞行中的构建**完成时会把旧样式章节重新 `insertChapter` 插回。
- 这些窗口里可能出现"旧页范围 + 新排版内容"的错配。
### 待办 / 复现所需
1. 确认触发操作:是否在设置面板调整行距/字号后(未关面板或刚关)翻页出现。
2. 确认重启 app 后是否自愈(受控实验表明缓存路径本身一致,理应自愈;若不自愈则有持久化的脏状态)。
3. 修复方向(待复现后定):设置变更代(generation)标记贯穿构建流程,飞行中的旧代构建完成后丢弃、不得插回 store;或 `chapterDataCache` 键加入 renderSignature。
### 经验教训
- 跨启动比较"第 N 页"截图无效:章节渐进加载过程中全局页码会漂移。
- 判断断点是否合法的快捷方法:数满行字数。断点不在行边界 ⟺ 范围与显示度量不一致。
---
## 相关工具与命令备忘
| 用途 | 方法 |
|---|---|
| 分页裁剪日志 | 启动参数 `--demo-pagination-debug` |
| 自动打开书籍/翻页 | `--demo-book-title 宝山 --demo-page N --demo-clear-cache --demo-reset-state` |
| 注入阅读设置 | `simctl spawn <sim> defaults write cn.shen.ReadViewDemo ssreader.epub.settings -data <hex(JSON)>`JSON 形如 `{"fontSize":15,"lineHeightMultiple":1.8}` |
| 截屏 | `xcrun simctl io <sim> screenshot <绝对路径>`(相对路径会因只读文件系统失败) |
| 模拟器 | 预装仅 16/17 系列;`simctl create` 可建 iPhone 15 Pro Max430×932@3x 与问题截图同尺寸) |
| 页边界/换行一致性校验 | 启动参数 `--demo-pagination-validate`RDEPUBTextPageBoundaryValidator,逐页比对显示换行与全章上下文换行,输出 STALE-RANGE / DISPLAY-DIVERGE / DIAGNOSE / ATTR-DIFF |
| 设置面板自动翻转 | 启动参数 `--demo-settings-flip`RDEPUBSettingsFlipAutomation,自动开面板→改行距→关面板→翻页,三种时序) |
---
## 问题二根因(2026-07-07 续查确认)
### 结论
**分页与显示使用同一引擎(DTCoreText/CoreText)但输入字符串不同:分页在“全章字符串”上下文中断行;显示把页内容取成子串后重新断行。`CTTypesetterSuggestLineBreak` 在同一文字、同一属性、同一宽度下,会因字符串上下文不同而在 CJK 标点压缩/悬挂处产生 ±1 字的断点漂移。** 页范围(全章上下文产物)与显示换行(子串产物)在页内任何一行发生漂移,累积到页尾就出现孤字(用户看到的「相」)或半空行。
与缓存、设置换表竞态无关:设置变更只是**搬动了页边界位置**,让某个边界恰好落在漂移点上,症状因此看似随设置变化出现/消失。这同时解释了此前所有观察:冷/热启动转储完全一致(分页自身是一致的,错配发生在分页 vs 显示);重启后"第 N 页"不复现(边界挪走了)。
### 证据(宝山书 spine 3,字号 15 / 行距 1.6 / 宽 398pt
`--demo-pagination-validate` 基线运行(无任何设置操作)即命中,前 8 页中 5 页分叉。决定性样本 page 8/55 第 11 行(行首 offset 4224):
- 显示端断点 4252(行尾"…可以相"——孤字机制当场复现),全章上下文断点 4253("…可以相当"
- 逐字符属性 diff**完全一致**(无 ATTR-DIFF
- 对照排版:原始子串 = 4252,段落级上下文 = 4252**只有全章上下文 = 4253**
- page 6/55 的上下文探针行宽 408.39 > 框宽 398 —— 标点悬挂/压缩越界的直接证据
即断点差异既不是首行缩进归一化(`normalizedPageContent` 的 firstLineHeadIndent 处理本身是正确的),也不是属性差异,而是 CoreText 断行对字符串上下文(跨段落!)敏感。
### 修复(方案 A2026-07-07 已实施)
显示端改为**全章上下文排版**`RDEPUBTextContentView.configure``page.chapterContent` 的整章副本作为显示内容,`DTCoreTextLayouter(章节副本).layoutFrame(bounds, range: page.contentRange)` 只排当页范围。断行与分页器按构造一致(同串、同起点、同宽)。
配套改动:
- layoutFrame 的 stringRange 因此为**章节绝对坐标**。`RDEPUBPageLayoutSnapshot.build` 去掉 +pageStartOffset 归一(本就输出绝对坐标给消费方),`RDEPUBPageInteractionController` 去掉全部 -pageOffset 反换算;选区/高亮/点击测试/装饰层的消费接口本来就是绝对坐标,无需变动。
- 主题前景色、暗色图片调整(`RDEPUBDarkImageAdjuster` 新增 `in range:` 参数)、高亮标记只施加在章节副本的当页范围上;`applyHighlightsToContent` 改用绝对 overlap 范围。
- 删除 DTCoreText 路径对 `normalizedPageContent` 的调用——续段首行缩进/段前距归一化 hack 由上下文排版天然取代(非 DTCoreText 回退路径仍保留)。
- layouterframesetter)随显示内容缓存于 cell(`coreTextLayouter`),bounds 变化只重建 layoutFrame,避免每次 layout pass 对整章重建 framesetter。
- `RDEPUBTextPageBoundaryValidator` 适配绝对坐标,保留为回归警报。
验证:`--demo-pagination-validate` 基线(原先 spine 3 前 8 页 5 处分叉)修复后 **0 命中**;第 10 页首行断点与分页一致(「…使用了更多」);Selection/Annotation UI 测试回归通过(见下)。
### 遗留(独立的潜在缺陷,本次未触发)
- `RDEPUBChapterRuntimeStore.chapterDataCache` 仅按 spineIndex 键控;设置变更时飞行中的旧构建完成后仍会插回(`RDEPUBChapterLoader.loadChapter``insertChapter` 无代际校验)。
- `RDEPUBChapterLoader.pendingLoads` 按 spineIndex 合流,跨设置变更合流会把旧样式章节交给新请求。
- 建议修复问题二后用 `--demo-settings-flip` + `--demo-pagination-validate` 回归验证这两个窗口。
+92 -92
View File
@@ -6,54 +6,54 @@
## 1. 模块概述 ## 1. 模块概述
`ReaderView` 是 ReadViewSDK 的**通用翻页容器层**,位于 EPUBUI 层之下。它提供与 EPUB 内容无关的页面展示、翻页动画、手势识别、页面预加载和双页布局能力。上层通过 `RDReaderPageProvider` 协议提供页面内容视图,ReaderView 负责容器管理和翻页调度。 `ReaderView` 是 ReadViewSDK 的**通用翻页容器层**,位于 EPUBUI 层之下。它提供与 EPUB 内容无关的页面展示、翻页动画、手势识别、页面预加载和双页布局能力。上层通过 `RDEpubReaderPageProvider` 协议提供页面内容视图,ReaderView 负责容器管理和翻页调度。
**文件清单(14 个 Swift 文件):** **文件清单(14 个 Swift 文件):**
| 文件 | 核心类型 | 职责 | | 文件 | 核心类型 | 职责 |
|------|----------|------| |------|----------|------|
| `RDReaderView.swift` | `RDReaderView` | 主容器视图,协调所有子组件 | | `RDEpubReaderView.swift` | `RDEpubReaderView` | 主容器视图,协调所有子组件 |
| `RDReaderViewProtocols.swift` | 协议 + 枚举 | 数据源、代理、导航协议定义 | | `RDEpubReaderViewProtocols.swift` | 协议 + 枚举 | 数据源、代理、导航协议定义 |
| `RDReaderFlowLayout.swift` | `RDReaderFlowLayout` | UICollectionView 自定义布局 | | `RDEpubReaderFlowLayout.swift` | `RDEpubReaderFlowLayout` | UICollectionView 自定义布局 |
| `RDReaderGestureController.swift` | `RDReaderGestureController` | 手势控制器(预留) | | `RDEpubReaderGestureController.swift` | `RDEpubReaderGestureController` | 手势控制器(预留) |
| `RDReaderContentCell.swift` | `RDReaderContentCell` | CollectionView 内容 Cell | | `RDEpubReaderContentCell.swift` | `RDEpubReaderContentCell` | CollectionView 内容 Cell |
| `RDReaderPageChildViewController.swift` | `RDReaderPageChildViewController` | PageCurl 模式子 VC | | `RDEpubReaderPageChildViewController.swift` | `RDEpubReaderPageChildViewController` | PageCurl 模式子 VC |
| `RDReaderView+PageCurl.swift` | Extension | UIPageViewController 数据源/代理 | | `RDEpubReaderView+PageCurl.swift` | Extension | UIPageViewController 数据源/代理 |
| `RDReaderView+CollectionView.swift` | Extension | UICollectionView 数据源/布局代理 | | `RDEpubReaderView+CollectionView.swift` | Extension | UICollectionView 数据源/布局代理 |
| `RDReaderView+ContentAccess.swift` | Extension | 内容视图访问与复用 | | `RDEpubReaderView+ContentAccess.swift` | Extension | 内容视图访问与复用 |
| `RDReaderView+ToolView.swift` | Extension | 工具栏安装与动画 | | `RDEpubReaderView+ToolView.swift` | Extension | 工具栏安装与动画 |
| `Paging/RDReaderPagingController.swift` | `RDReaderPagingController` | 翻页状态机 | | `Paging/RDEpubReaderPagingController.swift` | `RDEpubReaderPagingController` | 翻页状态机 |
| `Paging/RDReaderPreloadController.swift` | `RDReaderPreloadController` | 页面预加载与缓存 | | `Paging/RDEpubReaderPreloadController.swift` | `RDEpubReaderPreloadController` | 页面预加载与缓存 |
| `Paging/RDReaderSpreadResolver.swift` | `RDReaderSpreadResolver` | 双页展开计算 | | `Paging/RDEpubReaderSpreadResolver.swift` | `RDEpubReaderSpreadResolver` | 双页展开计算 |
| `Paging/RDReaderTapRegionHandler.swift` | `RDReaderTapRegionHandler` | 点击区域判定 | | `Paging/RDEpubReaderTapRegionHandler.swift` | `RDEpubReaderTapRegionHandler` | 点击区域判定 |
--- ---
## 2. 协议定义(RDReaderViewProtocols.swift ## 2. 协议定义(RDEpubReaderViewProtocols.swift
### 2.1 RDReaderDataSource(旧版数据源,已废弃) ### 2.1 RDEpubReaderDataSource(旧版数据源,已废弃)
```swift ```swift
@objc public protocol RDReaderDataSource: NSObjectProtocol { @objc public protocol RDEpubReaderDataSource: NSObjectProtocol {
func pageCountOfReaderView(readerView: RDReaderView) -> Int func pageCountOfReaderView(readerView: RDEpubReaderView) -> Int
func pageContentView(readerView: RDReaderView, pageNum: Int, containerView: UIView?) -> UIView func pageContentView(readerView: RDEpubReaderView, pageNum: Int, containerView: UIView?) -> UIView
func pageIdentifier(readerView: RDReaderView, pageNum: Int) -> String? func pageIdentifier(readerView: RDEpubReaderView, pageNum: Int) -> String?
@objc optional func topToolView(readerView: RDReaderView) -> UIView? @objc optional func topToolView(readerView: RDEpubReaderView) -> UIView?
@objc optional func bottomToolView(readerView: RDReaderView) -> UIView? @objc optional func bottomToolView(readerView: RDEpubReaderView) -> UIView?
} }
``` ```
> 向后兼容保留,新代码应使用 `RDReaderPageProvider`。 > 向后兼容保留,新代码应使用 `RDEpubReaderPageProvider`。
### 2.2 RDReaderPageProvider(推荐数据源) ### 2.2 RDEpubReaderPageProvider(推荐数据源)
```swift ```swift
@objc public protocol RDReaderPageProvider: NSObjectProtocol { @objc public protocol RDEpubReaderPageProvider: NSObjectProtocol {
func numberOfPages(in readerView: RDReaderView) -> Int func numberOfPages(in readerView: RDEpubReaderView) -> Int
func readerView(_ readerView: RDReaderView, viewForPageAt index: Int, reusableView: UIView?) -> UIView func readerView(_ readerView: RDEpubReaderView, viewForPageAt index: Int, reusableView: UIView?) -> UIView
@objc optional func pageIdentifier(in readerView: RDReaderView, index: Int) -> String? @objc optional func pageIdentifier(in readerView: RDEpubReaderView, index: Int) -> String?
@objc optional func readerViewTopChrome(_ readerView: RDReaderView) -> UIView? @objc optional func readerViewTopChrome(_ readerView: RDEpubReaderView) -> UIView?
@objc optional func readerViewBottomChrome(_ readerView: RDReaderView) -> UIView? @objc optional func readerViewBottomChrome(_ readerView: RDEpubReaderView) -> UIView?
} }
``` ```
@@ -65,31 +65,31 @@
| `readerViewTopChrome(_:)` | 返回顶部工具栏视图 | | `readerViewTopChrome(_:)` | 返回顶部工具栏视图 |
| `readerViewBottomChrome(_:)` | 返回底部工具栏视图 | | `readerViewBottomChrome(_:)` | 返回底部工具栏视图 |
### 2.3 RDReaderDelegate ### 2.3 RDEpubReaderDelegate
```swift ```swift
@objc public protocol RDReaderDelegate: NSObjectProtocol { @objc public protocol RDEpubReaderDelegate: NSObjectProtocol {
func pageNum(readerView: RDReaderView, pageNum: Int) func pageNum(readerView: RDEpubReaderView, pageNum: Int)
@objc optional func readerViewOrientationWillChange(readerView: RDReaderView, isLandscape: Bool) @objc optional func readerViewOrientationWillChange(readerView: RDEpubReaderView, isLandscape: Bool)
} }
``` ```
### 2.4 RDReaderPageNavigating ### 2.4 RDEpubReaderPageNavigating
```swift ```swift
public protocol RDReaderPageNavigating: AnyObject { public protocol RDEpubReaderPageNavigating: AnyObject {
var currentPage: Int { get } var currentPage: Int { get }
func reloadPages() func reloadPages()
func transition(to page: Int, animated: Bool) func transition(to page: Int, animated: Bool)
} }
``` ```
`RDReaderView` 遵循此协议,提供统一的页面导航接口。 `RDEpubReaderView` 遵循此协议,提供统一的页面导航接口。
### 2.5 枚举类型 ### 2.5 枚举类型
```swift ```swift
extension RDReaderView { extension RDEpubReaderView {
public enum DisplayType { public enum DisplayType {
case pageCurl // 仿UIPageViewController case pageCurl // 仿UIPageViewController
case horizontalScroll // UICollectionView case horizontalScroll // UICollectionView
@@ -105,13 +105,13 @@ extension RDReaderView {
--- ---
## 3. 核心类:RDReaderView ## 3. 核心类:RDEpubReaderView
**文件:** `RDReaderView.swift` **文件:** `RDEpubReaderView.swift`
`RDReaderView` 是一个 `UIView` 子类,作为翻页容器的主入口。内部管理两种翻页引擎: `RDEpubReaderView` 是一个 `UIView` 子类,作为翻页容器的主入口。内部管理两种翻页引擎:
- **PageCurl 模式**:使用 `UIPageViewController` 实现仿真翻页 - **PageCurl 模式**:使用 `UIPageViewController` 实现仿真翻页
- **Scroll 模式**:使用 `UICollectionView` + 自定义 `RDReaderFlowLayout` 实现滑动翻页 - **Scroll 模式**:使用 `UICollectionView` + 自定义 `RDEpubReaderFlowLayout` 实现滑动翻页
### 3.1 关键属性 ### 3.1 关键属性
@@ -124,16 +124,16 @@ extension RDReaderView {
| `coverPageIndex` | `Int?` | 封面页索引(独占一屏) | | `coverPageIndex` | `Int?` | 封面页索引(独占一屏) |
| `pagesPerScreen` | `Int` | 每屏页数(横屏双页时为 2) | | `pagesPerScreen` | `Int` | 每屏页数(横屏双页时为 2) |
| `preloadRadius` | `Int` | 预加载半径(默认 1 | | `preloadRadius` | `Int` | 预加载半径(默认 1 |
| `dataSource` | `RDReaderDataSource?` | 旧版数据源 | | `dataSource` | `RDEpubReaderDataSource?` | 旧版数据源 |
| `pageProvider` | `RDReaderPageProvider?` | 推荐数据源 | | `pageProvider` | `RDEpubReaderPageProvider?` | 推荐数据源 |
| `delegate` | `RDReaderDelegate?` | 事件代理 | | `delegate` | `RDEpubReaderDelegate?` | 事件代理 |
| `toolViewAnimationDuration` | `TimeInterval` | 工具栏动画时长(0.3s | | `toolViewAnimationDuration` | `TimeInterval` | 工具栏动画时长(0.3s |
### 3.2 关键方法 ### 3.2 关键方法
```swift ```swift
/// pageCurl / horizontalScroll / verticalScroll /// pageCurl / horizontalScroll / verticalScroll
public func switchReaderDisplayType(_ displayType: RDReaderView.DisplayType) public func switchReaderDisplayType(_ displayType: RDEpubReaderView.DisplayType)
/// ///
public func transitionToPage(pageNum: Int, animated: Bool = false) public func transitionToPage(pageNum: Int, animated: Bool = false)
@@ -154,22 +154,22 @@ public func isFullScreenPage(_ pageNum: Int) -> Bool
|------|------|------| |------|------|------|
| `pageViewController` | `UIPageViewController` | PageCurl 翻页引擎 | | `pageViewController` | `UIPageViewController` | PageCurl 翻页引擎 |
| `collectionView` | `UICollectionView` | Scroll 翻页引擎 | | `collectionView` | `UICollectionView` | Scroll 翻页引擎 |
| `layout` | `RDReaderFlowLayout` | CollectionView 自定义布局 | | `layout` | `RDEpubReaderFlowLayout` | CollectionView 自定义布局 |
| `spreadResolver` | `RDReaderSpreadResolver` | 双页配对计算 | | `spreadResolver` | `RDEpubReaderSpreadResolver` | 双页配对计算 |
| `tapRegionHandler` | `RDReaderTapRegionHandler` | 点击区域判定 | | `tapRegionHandler` | `RDEpubReaderTapRegionHandler` | 点击区域判定 |
| `preloadController` | `RDReaderPreloadController` | 页面预加载与缓存 | | `preloadController` | `RDEpubReaderPreloadController` | 页面预加载与缓存 |
| `pagingController` | `RDReaderPagingController` | 翻页状态管理 | | `pagingController` | `RDEpubReaderPagingController` | 翻页状态管理 |
--- ---
## 4. 翻页状态机:RDReaderPagingController ## 4. 翻页状态机:RDEpubReaderPagingController
**文件:** `Paging/RDReaderPagingController.swift` **文件:** `Paging/RDEpubReaderPagingController.swift`
管理 PageCurl 模式下的翻页请求队列,防止动画冲突。 管理 PageCurl 模式下的翻页请求队列,防止动画冲突。
```swift ```swift
struct RDReaderPagingController { struct RDEpubReaderPagingController {
struct PageTransitionRequest: Equatable { struct PageTransitionRequest: Equatable {
let pageNum: Int let pageNum: Int
let animated: Bool let animated: Bool
@@ -180,7 +180,7 @@ struct RDReaderPagingController {
var didBuildUI: Bool // UI var didBuildUI: Bool // UI
/// PageCurl true /// PageCurl true
mutating func shouldQueuePageTransition(_ request: PageTransitionRequest, currentDisplayType: RDReaderView.DisplayType) -> Bool mutating func shouldQueuePageTransition(_ request: PageTransitionRequest, currentDisplayType: RDEpubReaderView.DisplayType) -> Bool
/// ///
mutating func finishPageCurlTransition() -> PageTransitionRequest? mutating func finishPageCurlTransition() -> PageTransitionRequest?
@@ -192,9 +192,9 @@ struct RDReaderPagingController {
--- ---
## 5. 页面预加载:RDReaderPreloadController ## 5. 页面预加载:RDEpubReaderPreloadController
**文件:** `Paging/RDReaderPreloadController.swift` **文件:** `Paging/RDEpubReaderPreloadController.swift`
负责在当前页周围预渲染页面视图,减少翻页时的白屏时间。 负责在当前页周围预渲染页面视图,减少翻页时的白屏时间。
@@ -224,27 +224,27 @@ func invalidate(environment: Environment)
```swift ```swift
struct Environment { struct Environment {
let displayType: RDReaderView.DisplayType let displayType: RDEpubReaderView.DisplayType
let isLandscape: Bool let isLandscape: Bool
let pagesPerScreen: Int let pagesPerScreen: Int
let boundsSize: CGSize let boundsSize: CGSize
let landscapeDualPageEnabled: Bool let landscapeDualPageEnabled: Bool
let coverPageIndex: Int? let coverPageIndex: Int?
let totalPages: Int let totalPages: Int
let spreadResolver: RDReaderSpreadResolver let spreadResolver: RDEpubReaderSpreadResolver
} }
``` ```
--- ---
## 6. 双页展开计算:RDReaderSpreadResolver ## 6. 双页展开计算:RDEpubReaderSpreadResolver
**文件:** `Paging/RDReaderSpreadResolver.swift` **文件:** `Paging/RDEpubReaderSpreadResolver.swift`
纯函数式结构体,负责双页模式下的页面配对和导航计算。 纯函数式结构体,负责双页模式下的页面配对和导航计算。
```swift ```swift
struct RDReaderSpreadResolver { struct RDEpubReaderSpreadResolver {
/// ///
func isFullScreenPage(_ pageNum: Int, landscapeDualPageEnabled: Bool, isLandscape: Bool, coverPageIndex: Int?) -> Bool func isFullScreenPage(_ pageNum: Int, landscapeDualPageEnabled: Bool, isLandscape: Bool, coverPageIndex: Int?) -> Bool
@@ -265,15 +265,15 @@ struct RDReaderSpreadResolver {
--- ---
## 7. 点击区域判定:RDReaderTapRegionHandler ## 7. 点击区域判定:RDEpubReaderTapRegionHandler
**文件:** `Paging/RDReaderTapRegionHandler.swift` **文件:** `Paging/RDEpubReaderTapRegionHandler.swift`
将屏幕三等分,判定点击属于左/中/右区域。 将屏幕三等分,判定点击属于左/中/右区域。
```swift ```swift
struct RDReaderTapRegionHandler { struct RDEpubReaderTapRegionHandler {
func resolveTapEvent(point: CGPoint, viewFrame: CGRect, isToolViewVisible: Bool) -> RDReaderView.TapEvent func resolveTapEvent(point: CGPoint, viewFrame: CGRect, isToolViewVisible: Bool) -> RDEpubReaderView.TapEvent
} }
``` ```
@@ -284,9 +284,9 @@ struct RDReaderTapRegionHandler {
--- ---
## 8. 流式布局:RDReaderFlowLayout ## 8. 流式布局:RDEpubReaderFlowLayout
**文件:** `RDReaderFlowLayout.swift` **文件:** `RDEpubReaderFlowLayout.swift`
`UICollectionViewFlowLayout` 子类,支持水平滚动和垂直滚动两种模式。 `UICollectionViewFlowLayout` 子类,支持水平滚动和垂直滚动两种模式。
@@ -294,7 +294,7 @@ struct RDReaderTapRegionHandler {
| 属性 | 类型 | 说明 | | 属性 | 类型 | 说明 |
|------|------|------| |------|------|------|
| `displayType` | `RDReaderView.DisplayType` | 布局模式 | | `displayType` | `RDEpubReaderView.DisplayType` | 布局模式 |
| `isLandscapeDualPage` | `Bool` | 是否横屏双页 | | `isLandscapeDualPage` | `Bool` | 是否横屏双页 |
| `coverPageIndex` | `Int?` | 封面页索引 | | `coverPageIndex` | `Int?` | 封面页索引 |
| `pagesPerScreen` | `Int` | 每屏页数 | | `pagesPerScreen` | `Int` | 每屏页数 |
@@ -302,12 +302,12 @@ struct RDReaderTapRegionHandler {
### 8.2 协议 ### 8.2 协议
```swift ```swift
public protocol RDReaderFlowLayoutDataSoure: NSObjectProtocol { public protocol RDEpubReaderFlowLayoutDataSoure: NSObjectProtocol {
func heigtOfVerticalScrollPage(flowLayout: RDReaderFlowLayout, pageIndex: Int) -> CGFloat? func heigtOfVerticalScrollPage(flowLayout: RDEpubReaderFlowLayout, pageIndex: Int) -> CGFloat?
} }
@objc public protocol RDReaderFlowLayoutDelegate: NSObjectProtocol { @objc public protocol RDEpubReaderFlowLayoutDelegate: NSObjectProtocol {
func pageNum(flowLayout: RDReaderFlowLayout, pageIndex: Int) func pageNum(flowLayout: RDEpubReaderFlowLayout, pageIndex: Int)
} }
``` ```
@@ -324,28 +324,28 @@ func currentContentOffset(count: Int) -> CGPoint
--- ---
## 9. 内容 CellRDReaderContentCell ## 9. 内容 CellRDEpubReaderContentCell
**文件:** `RDReaderContentCell.swift` **文件:** `RDEpubReaderContentCell.swift`
`UICollectionViewCell` 子类,用于 Scroll 模式下承载页面内容视图。 `UICollectionViewCell` 子类,用于 Scroll 模式下承载页面内容视图。
```swift ```swift
class RDReaderContentCell: UICollectionViewCell { class RDEpubReaderContentCell: UICollectionViewCell {
var containerView: UIView? // contentView var containerView: UIView? // contentView
} }
``` ```
--- ---
## 10. PageCurl 子控制器:RDReaderPageChildViewController ## 10. PageCurl 子控制器:RDEpubReaderPageChildViewController
**文件:** `RDReaderPageChildViewController.swift` **文件:** `RDEpubReaderPageChildViewController.swift`
`UIViewController` 子类,作为 `UIPageViewController` 的页面 VC。 `UIViewController` 子类,作为 `UIPageViewController` 的页面 VC。
```swift ```swift
class RDReaderPageChildViewController: UIViewController { class RDEpubReaderPageChildViewController: UIViewController {
var contentView: UIView? // var contentView: UIView? //
var pageNum: Int // var pageNum: Int //
@@ -357,7 +357,7 @@ class RDReaderPageChildViewController: UIViewController {
## 11. Extension 汇总 ## 11. Extension 汇总
### 11.1 RDReaderView+PageCurl ### 11.1 RDEpubReaderView+PageCurl
实现 `UIPageViewControllerDataSource``UIPageViewControllerDelegate` 实现 `UIPageViewControllerDataSource``UIPageViewControllerDelegate`
@@ -367,18 +367,18 @@ class RDReaderPageChildViewController: UIViewController {
- `pageViewController(_:willTransitionTo:)` — 即将翻页时预加载 - `pageViewController(_:willTransitionTo:)` — 即将翻页时预加载
**特殊页码:** **特殊页码:**
- `RDReaderView.blankPageNum``Int.max`)— 双页模式下的空白页 - `RDEpubReaderView.blankPageNum``Int.max`)— 双页模式下的空白页
- `RDReaderView.blankEndPageNum``Int.max - 1`)— 末尾空白页 - `RDEpubReaderView.blankEndPageNum``Int.max - 1`)— 末尾空白页
### 11.2 RDReaderView+CollectionView ### 11.2 RDEpubReaderView+CollectionView
实现 `UICollectionViewDataSource``RDReaderFlowLayoutDelegate``RDReaderFlowLayoutDataSoure` 实现 `UICollectionViewDataSource``RDEpubReaderFlowLayoutDelegate``RDEpubReaderFlowLayoutDataSoure`
- `collectionView(_:cellForItemAt:)` — 复用预加载视图或创建新 Cell - `collectionView(_:cellForItemAt:)` — 复用预加载视图或创建新 Cell
- `collectionView(_:numberOfItemsInSection:)` — 返回总页数 - `collectionView(_:numberOfItemsInSection:)` — 返回总页数
- `pageNum(flowLayout:pageIndex:)` — 滚动时更新当前页码 - `pageNum(flowLayout:pageIndex:)` — 滚动时更新当前页码
### 11.3 RDReaderView+ContentAccess ### 11.3 RDEpubReaderView+ContentAccess
提供内容视图的注册、复用和查询: 提供内容视图的注册、复用和查询:
@@ -396,7 +396,7 @@ public func pageContentView(pageNum: Int) -> UIView?
public func resolvedSinglePageSize(pageNum: Int? = nil) -> CGSize public func resolvedSinglePageSize(pageNum: Int? = nil) -> CGSize
``` ```
### 11.4 RDReaderView+ToolView ### 11.4 RDEpubReaderView+ToolView
管理顶部/底部工具栏的安装、显示/隐藏动画: 管理顶部/底部工具栏的安装、显示/隐藏动画:
@@ -420,11 +420,11 @@ func updateToolViewHeightConstraintsIfNeeded()
| 模式 | 应用 | | 模式 | 应用 |
|------|------| |------|------|
| **策略模式** | `DisplayType` 切换 PageCurl / Scroll 两种翻页策略 | | **策略模式** | `DisplayType` 切换 PageCurl / Scroll 两种翻页策略 |
| **适配器模式** | `RDReaderLegacyDataSourceAdapter` 将旧 `RDReaderDataSource` 适配为 `RDReaderPageProvider` | | **适配器模式** | `RDEpubReaderLegacyDataSourceAdapter` 将旧 `RDEpubReaderDataSource` 适配为 `RDEpubReaderPageProvider` |
| **命令队列** | `RDReaderPagingController` 管理翻页请求队列 | | **命令队列** | `RDEpubReaderPagingController` 管理翻页请求队列 |
| **缓存签名** | `RDReaderPreloadController.CacheSignature` 检测环境变化自动失效 | | **缓存签名** | `RDEpubReaderPreloadController.CacheSignature` 检测环境变化自动失效 |
| **关注点分离** | Extension 将不同功能拆分到独立文件 | | **关注点分离** | Extension 将不同功能拆分到独立文件 |
| **纯函数** | `RDReaderSpreadResolver``RDReaderTapRegionHandler` 无状态计算 | | **纯函数** | `RDEpubReaderSpreadResolver``RDEpubReaderTapRegionHandler` 无状态计算 |
--- ---
@@ -434,7 +434,7 @@ func updateToolViewHeightConstraintsIfNeeded()
用户点击屏幕 用户点击屏幕
RDReaderTapRegionHandler.resolveTapEvent() RDEpubReaderTapRegionHandler.resolveTapEvent()
├── .left → goPreviousPage() ──→ spreadResolver.nextPage(forward: false) ├── .left → goPreviousPage() ──→ spreadResolver.nextPage(forward: false)
├── .right → goNextPage() ──→ spreadResolver.nextPage(forward: true) ├── .right → goNextPage() ──→ spreadResolver.nextPage(forward: true)
+4 -4
View File
@@ -64,13 +64,13 @@
### CocoaPods 依赖准备 ### CocoaPods 依赖准备
> 仓库包含示例工程 `ReadViewDemo`,并已提交 `Pods/` 与 `Podfile.lock`。如本地环境未同步,可在仓库根或示例工程目录运行: > 仓库包含示例工程 `ReadViewDemo`,并已提交 `ReadViewDemo/Pods/` 与 `ReadViewDemo/Podfile.lock`。如本地环境未同步,请在示例工程目录运行:
```bash ```bash
pod install cd ReadViewDemo && pod install
``` ```
(依赖入口:`Podfile``ReadViewDemo/Podfile` (依赖入口:`ReadViewDemo/Podfile`
### 运行 UI 测试 ### 运行 UI 测试
@@ -116,7 +116,7 @@ xcodebuild test \
建议同时记录以下观测项: 建议同时记录以下观测项:
- `prepareOnDemandChapter` 主线程 wall clock - `prepareOnDemandChapter` 主线程 wall clock
- `RDReaderPreloadController` 预加载命中率 - `RDEpubReaderPreloadController` 预加载命中率
- `bookPageMap` partial extension / full replacement 次数 - `bookPageMap` partial extension / full replacement 次数
- 页面静态底图缓存命中率 - 页面静态底图缓存命中率
- CFI 延迟构建完成次数与耗时 - CFI 延迟构建完成次数与耗时
+1 -1
View File
@@ -16,7 +16,7 @@
**输出**`RDEPUBTypesettingOutput`(渲染请求、诊断信息、兼容性报告) **输出**`RDEPUBTypesettingOutput`(渲染请求、诊断信息、兼容性报告)
**关键文件**`Sources/RDReaderView/EPUBTextRendering/Typesetter/` **关键文件**`Sources/RDEpubReaderView/EPUBTextRendering/Typesetter/`
--- ---
+28 -28
View File
@@ -31,12 +31,12 @@ graph TB
IndexTable[RDEPUBTextIndexTable] IndexTable[RDEPUBTextIndexTable]
end end
subgraph RDReaderView subgraph RDEpubReaderView
ReaderView[RDReaderView] ReaderView[RDEpubReaderView]
FlowLayout[RDReaderFlowLayout] FlowLayout[RDEpubReaderFlowLayout]
Preload[RDReaderPreloadController] Preload[RDEpubReaderPreloadController]
Spread[RDReaderSpreadResolver] Spread[RDEpubReaderSpreadResolver]
TapRegion[RDReaderTapRegionHandler] TapRegion[RDEpubReaderTapRegionHandler]
end end
subgraph EPUBUI subgraph EPUBUI
@@ -297,11 +297,11 @@ classDiagram
```mermaid ```mermaid
classDiagram classDiagram
class RDReaderView { class RDEpubReaderView {
+currentPage: Int +currentPage: Int
+currentDisplayType: DisplayType +currentDisplayType: DisplayType
+pageProvider: RDReaderPageProvider? +pageProvider: RDEpubReaderPageProvider?
+delegate: RDReaderDelegate? +delegate: RDEpubReaderDelegate?
+landscapeDualPageEnabled: Bool +landscapeDualPageEnabled: Bool
+pageDirection: PageDirection +pageDirection: PageDirection
+coverPageIndex: Int? +coverPageIndex: Int?
@@ -318,14 +318,14 @@ classDiagram
verticalScroll verticalScroll
} }
class RDReaderPageProvider { class RDEpubReaderPageProvider {
<<protocol>> <<protocol>>
+numberOfPages(in:) Int +numberOfPages(in:) Int
+readerView(_:viewForPageAt:reusableView:) UIView +readerView(_:viewForPageAt:reusableView:) UIView
+pageIdentifier(in:index:) String? +pageIdentifier(in:index:) String?
} }
class RDReaderFlowLayout { class RDEpubReaderFlowLayout {
+displayType: DisplayType +displayType: DisplayType
+isLandscapeDualPage: Bool +isLandscapeDualPage: Bool
+coverPageIndex: Int? +coverPageIndex: Int?
@@ -333,7 +333,7 @@ classDiagram
+currentPage: Int +currentPage: Int
} }
class RDReaderPreloadController { class RDEpubReaderPreloadController {
+radius: Int +radius: Int
+pageViewForDisplay(pageNum:environment:contentViewProvider:) UIView? +pageViewForDisplay(pageNum:environment:contentViewProvider:) UIView?
+takePreloadedView(for:) UIView? +takePreloadedView(for:) UIView?
@@ -341,13 +341,13 @@ classDiagram
+invalidate(environment:) +invalidate(environment:)
} }
class RDReaderSpreadResolver { class RDEpubReaderSpreadResolver {
+isFullScreenPage(...) Bool +isFullScreenPage(...) Bool
+dualPagePair(for:totalPages:coverPageIndex:) (Int, Int?) +dualPagePair(for:totalPages:coverPageIndex:) (Int, Int?)
+nextPage(from:totalPages:pagesPerScreen:coverPageIndex:forward:) Int? +nextPage(from:totalPages:pagesPerScreen:coverPageIndex:forward:) Int?
} }
class RDReaderTapRegionHandler { class RDEpubReaderTapRegionHandler {
+resolveTapEvent(point:viewFrame:isToolViewVisible:) TapEvent +resolveTapEvent(point:viewFrame:isToolViewVisible:) TapEvent
} }
@@ -359,7 +359,7 @@ classDiagram
right right
} }
class RDReaderPagingController { class RDEpubReaderPagingController {
+isTransitioning: Bool +isTransitioning: Bool
+didBuildUI: Bool +didBuildUI: Bool
+pendingTransitionRequest: PageTransitionRequest? +pendingTransitionRequest: PageTransitionRequest?
@@ -367,25 +367,25 @@ classDiagram
+finishPageCurlTransition() PageTransitionRequest? +finishPageCurlTransition() PageTransitionRequest?
} }
class RDReaderContentCell { class RDEpubReaderContentCell {
+containerView: UIView? +containerView: UIView?
} }
class RDReaderPageChildViewController { class RDEpubReaderPageChildViewController {
+contentView: UIView? +contentView: UIView?
+pageNum: Int +pageNum: Int
} }
RDReaderView --> DisplayType : uses RDEpubReaderView --> DisplayType : uses
RDReaderView --> RDReaderPageProvider : delegates RDEpubReaderView --> RDEpubReaderPageProvider : delegates
RDReaderView --> RDReaderFlowLayout : owns RDEpubReaderView --> RDEpubReaderFlowLayout : owns
RDReaderView --> RDReaderPreloadController : owns RDEpubReaderView --> RDEpubReaderPreloadController : owns
RDReaderView --> RDReaderSpreadResolver : owns RDEpubReaderView --> RDEpubReaderSpreadResolver : owns
RDReaderView --> RDReaderTapRegionHandler : owns RDEpubReaderView --> RDEpubReaderTapRegionHandler : owns
RDReaderView --> RDReaderPagingController : owns RDEpubReaderView --> RDEpubReaderPagingController : owns
RDReaderView --> RDReaderContentCell : creates RDEpubReaderView --> RDEpubReaderContentCell : creates
RDReaderView --> RDReaderPageChildViewController : creates RDEpubReaderView --> RDEpubReaderPageChildViewController : creates
RDReaderTapRegionHandler --> TapEvent : produces RDEpubReaderTapRegionHandler --> TapEvent : produces
``` ```
--- ---
@@ -403,7 +403,7 @@ classDiagram
+highlights: [RDEPUBHighlight] +highlights: [RDEPUBHighlight]
+bookmarks: [RDEPUBBookmark] +bookmarks: [RDEPUBBookmark]
+tableOfContents: [EPUBTableOfContentsItem] +tableOfContents: [EPUBTableOfContentsItem]
+readerView: RDReaderView +readerView: RDEpubReaderView
+readerContext: RDEPUBReaderContext +readerContext: RDEPUBReaderContext
+runtime: RDEPUBReaderRuntime? +runtime: RDEPUBReaderRuntime?
} }
+3 -1
View File
@@ -37,6 +37,8 @@
| [CHAPTER_RUNTIME.md](CHAPTER_RUNTIME.md) | 章节运行时详解:按需加载、章节窗口协调、页图管理、磁盘缓存、后台补全 | | [CHAPTER_RUNTIME.md](CHAPTER_RUNTIME.md) | 章节运行时详解:按需加载、章节窗口协调、页图管理、磁盘缓存、后台补全 |
| [CFI_SUBSYSTEM.md](CFI_SUBSYSTEM.md) | CFI 子系统详解:EPUB CFI 解析、生成、序列化、范围、恢复引擎 | | [CFI_SUBSYSTEM.md](CFI_SUBSYSTEM.md) | CFI 子系统详解:EPUB CFI 解析、生成、序列化、范围、恢复引擎 |
| [CFI_ISSUES_REVIEW.md](CFI_ISSUES_REVIEW.md) | CFI 实现问题分析报告:11 个问题(3 高 / 5 中 / 3 低),含修复优先级建议 | | [CFI_ISSUES_REVIEW.md](CFI_ISSUES_REVIEW.md) | CFI 实现问题分析报告:11 个问题(3 高 / 5 中 / 3 低),含修复优先级建议 |
| [PAGINATION_ISSUES_2026-07-06.md](PAGINATION_ISSUES_2026-07-06.md) | 分页问题调查记录:脚注图标 avoid-trim 裁行(已修复)+页范围/显示度量错配行中断页(待复现),含调试命令备忘 |
| [LONG_CHAPTER_MEMORY_OPTIMIZATION_PLAN.md](LONG_CHAPTER_MEMORY_OPTIMIZATION_PLAN.md) | 长章节内存优化低风险实施方案:在保留整章上下文布局正确性的前提下,分阶段收敛整章副本、共享 layouter、治理缓存与图片内存 |
| [API_REFERENCE.md](API_REFERENCE.md) | 公开 API 参考:RDEPUBReaderController 公开接口、委托协议、配置模型 | | [API_REFERENCE.md](API_REFERENCE.md) | 公开 API 参考:RDEPUBReaderController 公开接口、委托协议、配置模型 |
| [当前阅读器问题修复开发清单.md](当前阅读器问题修复开发清单.md) | 基于当前代码实现整理的逐任务开发计划:10 个任务、修改位置、实施步骤、风险点与验收标准 | | [当前阅读器问题修复开发清单.md](当前阅读器问题修复开发清单.md) | 基于当前代码实现整理的逐任务开发计划:10 个任务、修改位置、实施步骤、风险点与验收标准 |
| [大书远距目录跳转与后台补全优化方案.md](大书远距目录跳转与后台补全优化方案.md) | 面向大书按需分页模式的完整优化方案:远距目录跳转、后台补全优先级、页图接管协议与分阶段实施路径 | | [大书远距目录跳转与后台补全优化方案.md](大书远距目录跳转与后台补全优化方案.md) | 面向大书按需分页模式的完整优化方案:远距目录跳转、后台补全优先级、页图接管协议与分阶段实施路径 |
@@ -45,7 +47,7 @@
## 项目信息 ## 项目信息
- **模块总数:** 4 个(EPUBCore、EPUBTextRendering、RDReaderView、EPUBUI - **模块总数:** 4 个(EPUBCore、EPUBTextRendering、RDEpubReaderView、EPUBUI
- **Swift 文件数:** 142 个(SDK Sources - **Swift 文件数:** 142 个(SDK Sources
- **测试用例数:** 23 个测试类,约 99 个测试方法(UI 测试) - **测试用例数:** 23 个测试类,约 99 个测试方法(UI 测试)
- **最低 iOS 版本:** 15.6 - **最低 iOS 版本:** 15.6
-30
View File
@@ -1,30 +0,0 @@
platform :ios, '15.6'
use_frameworks!
workspace 'ReadViewSDK.xcworkspace'
project 'ReadViewSDK.xcodeproj'
target 'ReadViewSDK' do
pod 'RDReaderView', :path => '..'
end
post_install do |installer|
apply_settings = lambda do |config|
config.build_settings['ENABLE_USER_SCRIPT_SANDBOXING'] = 'NO'
config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '15.6'
end
# Pods project (Pods.xcodeproj)
installer.pods_project.build_configurations.each { |c| apply_settings.call(c) }
installer.pods_project.targets.each { |t| t.build_configurations.each { |c| apply_settings.call(c) } }
installer.aggregate_targets.each do |at|
p = at.user_project
next unless p
# User project(s) that integrate Pods (e.g. ReadViewDemo / ReadViewSDK)
p.build_configurations.each { |c| apply_settings.call(c) }
p.targets.each { |t| t.build_configurations.each { |c| apply_settings.call(c) } }
p.save
end
end
+19
View File
@@ -0,0 +1,19 @@
Pod::Spec.new do |s|
s.name = "RDEpubReaderView"
s.module_name = "RDEpubReaderView"
s.version = "0.0.1"
s.summary = "A reader view for EPUB and plain-text books"
s.platform = :ios, "15.0"
s.swift_versions = ["5.10"]
s.homepage = "http://192.168.21.200:8418/4v5u09Z5a4Yuc/ReadViewSDK.git"
s.author = { "shenlei" => "shenlei@touchread.com" }
s.source = { :git => "http://192.168.21.200:8418/4v5u09Z5a4Yuc/ReadViewSDK.git", :tag => "#{s.version}" }
s.license = "MIT"
s.source_files = "Sources/RDEpubReaderView/**/*.{swift}"
s.resource_bundles = {
"RDEpubReaderViewAssets" => ["Sources/RDEpubReaderView/EPUBCore/Resources/**/*"]
}
s.dependency "ZIPFoundation", "~> 0.9"
s.dependency "DTCoreText", "~> 1.6"
s.requires_arc = true
end
-21
View File
@@ -1,21 +0,0 @@
Pod::Spec.new do |s|
s.name = "RDReaderView"
s.version = "0.0.1"
s.summary = "A reader view for novel"
s.platform = :ios, "15.6"
s.swift_versions = ["5.10"]
s.homepage = "https://github.com/namesubai/RDReaderView.git"
s.author = { "subai" => "804663401@qq.com" }
s.source = { :git => "https://github.com/namesubai/RDReaderView.git", :tag => "#{s.version}"}
s.license = "MIT"
s.source_files = 'Sources/RDReaderView/**/*.{swift}'
s.resource_bundles = {
'RDReaderViewAssets' => ['Sources/RDReaderView/EPUBCore/Resources/**/*']
}
s.dependency 'ZIPFoundation', '~> 0.9'
s.dependency 'DTCoreText', '~> 1.6'
s.requires_arc = true
end
+3 -2
View File
@@ -3,14 +3,15 @@ platform :ios, '15.6'
target 'ReadViewDemo' do target 'ReadViewDemo' do
# Comment the next line if you don't want to use dynamic frameworks # Comment the next line if you don't want to use dynamic frameworks
use_frameworks! use_frameworks!
pod 'RDReaderView', :path => '..' pod 'RDEpubReaderView', :path => '../Sources/RDEpubReaderView'
pod 'RDPDFReaderView', :path => '../Sources/RDPDFReaderView'
end end
post_install do |installer| post_install do |installer|
apply_settings = lambda do |config| apply_settings = lambda do |config|
config.build_settings['ENABLE_USER_SCRIPT_SANDBOXING'] = 'NO' config.build_settings['ENABLE_USER_SCRIPT_SANDBOXING'] = 'NO'
config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '15.6' config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '15.0'
end end
# Pods project (Pods.xcodeproj) # Pods project (Pods.xcodeproj)
+16 -6
View File
@@ -16,30 +16,40 @@ PODS:
- DTFoundation/Core - DTFoundation/Core
- DTFoundation/UIKit (1.7.19): - DTFoundation/UIKit (1.7.19):
- DTFoundation/Core - DTFoundation/Core
- RDReaderView (0.0.1): - RDEpubReaderView (0.0.2):
- DTCoreText (~> 1.6) - DTCoreText (~> 1.6)
- SnapKit (~> 5.7)
- ZIPFoundation (~> 0.9) - ZIPFoundation (~> 0.9)
- RDPDFReaderView (0.0.1):
- SnapKit (~> 5.7)
- SnapKit (5.7.1)
- ZIPFoundation (0.9.20) - ZIPFoundation (0.9.20)
DEPENDENCIES: DEPENDENCIES:
- RDReaderView (from `..`) - RDEpubReaderView (from `../Sources/RDEpubReaderView`)
- RDPDFReaderView (from `../Sources/RDPDFReaderView`)
SPEC REPOS: SPEC REPOS:
trunk: trunk:
- DTCoreText - DTCoreText
- DTFoundation - DTFoundation
- SnapKit
- ZIPFoundation - ZIPFoundation
EXTERNAL SOURCES: EXTERNAL SOURCES:
RDReaderView: RDEpubReaderView:
:path: ".." :path: "../Sources/RDEpubReaderView"
RDPDFReaderView:
:path: "../Sources/RDPDFReaderView"
SPEC CHECKSUMS: SPEC CHECKSUMS:
DTCoreText: 11b7fe2104f476f82e75a4e3dbdde74d7186cecb DTCoreText: 11b7fe2104f476f82e75a4e3dbdde74d7186cecb
DTFoundation: 76b624967cf5bcaae6bb057d622c536c36ef36d0 DTFoundation: 76b624967cf5bcaae6bb057d622c536c36ef36d0
RDReaderView: 2e0eeeff4bcfe8bbc09642344d326440af280ed5 RDEpubReaderView: 5a09a7856f04b6f2f8610aa6fd965d35cadb8e17
RDPDFReaderView: f7c2d0ea7c129aa4ab15ae7f9daad2974573d749
SnapKit: d612e99e678a2d3b95bf60b0705ed0a35c03484a
ZIPFoundation: dfd3d681c4053ff7e2f7350bc4e53b5dba3f5351 ZIPFoundation: dfd3d681c4053ff7e2f7350bc4e53b5dba3f5351
PODFILE CHECKSUM: 775f5c8c488024e24d494aad6331e9fef8f9e2e5 PODFILE CHECKSUM: c39db27dbc9c6a96a33f22e78ce1b22dcd48d75f
COCOAPODS: 1.16.2 COCOAPODS: 1.16.2
@@ -0,0 +1,40 @@
{
"name": "RDEpubReaderView",
"module_name": "RDEpubReaderView",
"version": "0.0.2",
"summary": "A reader view for EPUB, TXT, MOBI, and CBZ books",
"platforms": {
"ios": "15.0"
},
"swift_versions": [
"5.10"
],
"homepage": "http://192.168.21.200:8418/4v5u09Z5a4Yuc/ReadViewSDK.git",
"authors": {
"shenlei": "shenlei@touchread.com"
},
"source": {
"git": "http://192.168.21.200:8418/4v5u09Z5a4Yuc/ReadViewSDK.git",
"tag": "0.0.2"
},
"license": "MIT",
"source_files": "**/*.swift",
"resource_bundles": {
"RDEpubReaderViewAssets": [
"EPUBCore/Resources/**/*"
]
},
"dependencies": {
"ZIPFoundation": [
"~> 0.9"
],
"DTCoreText": [
"~> 1.6"
],
"SnapKit": [
"~> 5.7"
]
},
"requires_arc": true,
"swift_version": "5.10"
}
@@ -0,0 +1,33 @@
{
"name": "RDPDFReaderView",
"module_name": "RDPDFReaderView",
"version": "0.0.1",
"summary": "UIKit PDF reader supporting host images and direct PDFKit parsing",
"platforms": {
"ios": "15.0"
},
"swift_versions": [
"5.10"
],
"homepage": "https://example.invalid/RDPDFReaderView",
"authors": {
"readoor": "ios@touchread.com"
},
"source": {
"path": "."
},
"license": "MIT",
"source_files": "{Sources,ReaderView}/**/*.swift",
"dependencies": {
"SnapKit": [
"~> 5.7"
]
},
"frameworks": [
"Vision",
"CoreImage",
"PDFKit"
],
"requires_arc": true,
"swift_version": "5.10"
}
@@ -1,36 +0,0 @@
{
"name": "RDReaderView",
"version": "0.0.1",
"summary": "A reader view for novel",
"platforms": {
"ios": "15.6"
},
"swift_versions": [
"5.10"
],
"homepage": "https://github.com/namesubai/RDReaderView.git",
"authors": {
"subai": "804663401@qq.com"
},
"source": {
"git": "https://github.com/namesubai/RDReaderView.git",
"tag": "0.0.1"
},
"license": "MIT",
"source_files": "Sources/RDReaderView/**/*.{swift}",
"resource_bundles": {
"RDReaderViewAssets": [
"Sources/RDReaderView/EPUBCore/Resources/**/*"
]
},
"dependencies": {
"ZIPFoundation": [
"~> 0.9"
],
"DTCoreText": [
"~> 1.6"
]
},
"requires_arc": true,
"swift_version": "5.10"
}
+16 -6
View File
@@ -16,30 +16,40 @@ PODS:
- DTFoundation/Core - DTFoundation/Core
- DTFoundation/UIKit (1.7.19): - DTFoundation/UIKit (1.7.19):
- DTFoundation/Core - DTFoundation/Core
- RDReaderView (0.0.1): - RDEpubReaderView (0.0.2):
- DTCoreText (~> 1.6) - DTCoreText (~> 1.6)
- SnapKit (~> 5.7)
- ZIPFoundation (~> 0.9) - ZIPFoundation (~> 0.9)
- RDPDFReaderView (0.0.1):
- SnapKit (~> 5.7)
- SnapKit (5.7.1)
- ZIPFoundation (0.9.20) - ZIPFoundation (0.9.20)
DEPENDENCIES: DEPENDENCIES:
- RDReaderView (from `..`) - RDEpubReaderView (from `../Sources/RDEpubReaderView`)
- RDPDFReaderView (from `../Sources/RDPDFReaderView`)
SPEC REPOS: SPEC REPOS:
trunk: trunk:
- DTCoreText - DTCoreText
- DTFoundation - DTFoundation
- SnapKit
- ZIPFoundation - ZIPFoundation
EXTERNAL SOURCES: EXTERNAL SOURCES:
RDReaderView: RDEpubReaderView:
:path: ".." :path: "../Sources/RDEpubReaderView"
RDPDFReaderView:
:path: "../Sources/RDPDFReaderView"
SPEC CHECKSUMS: SPEC CHECKSUMS:
DTCoreText: 11b7fe2104f476f82e75a4e3dbdde74d7186cecb DTCoreText: 11b7fe2104f476f82e75a4e3dbdde74d7186cecb
DTFoundation: 76b624967cf5bcaae6bb057d622c536c36ef36d0 DTFoundation: 76b624967cf5bcaae6bb057d622c536c36ef36d0
RDReaderView: 2e0eeeff4bcfe8bbc09642344d326440af280ed5 RDEpubReaderView: 5a09a7856f04b6f2f8610aa6fd965d35cadb8e17
RDPDFReaderView: f7c2d0ea7c129aa4ab15ae7f9daad2974573d749
SnapKit: d612e99e678a2d3b95bf60b0705ed0a35c03484a
ZIPFoundation: dfd3d681c4053ff7e2f7350bc4e53b5dba3f5351 ZIPFoundation: dfd3d681c4053ff7e2f7350bc4e53b5dba3f5351
PODFILE CHECKSUM: 775f5c8c488024e24d494aad6331e9fef8f9e2e5 PODFILE CHECKSUM: c39db27dbc9c6a96a33f22e78ce1b22dcd48d75f
COCOAPODS: 1.16.2 COCOAPODS: 1.16.2
File diff suppressed because it is too large Load Diff
+19
View File
@@ -0,0 +1,19 @@
Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
+155
View File
@@ -0,0 +1,155 @@
<img src="https://snapkit.github.io/SnapKit/images/banner.jpg" alt="" />
SnapKit is a DSL to make Auto Layout easy on both iOS and OS X.
[![Build Status](https://travis-ci.org/SnapKit/SnapKit.svg)](https://travis-ci.org/SnapKit/SnapKit)
[![Platform](https://img.shields.io/cocoapods/p/SnapKit.svg?style=flat)](https://github.com/SnapKit/SnapKit)
[![Cocoapods Compatible](https://img.shields.io/cocoapods/v/SnapKit.svg)](https://cocoapods.org/pods/SnapKit)
[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)
#### ⚠️ **To use with Swift 4.x please ensure you are using >= 4.0.0** ⚠️
#### ⚠️ **To use with Swift 5.x please ensure you are using >= 5.0.0** ⚠️
## Contents
- [Requirements](#requirements)
- [Migration Guides](#migration-guides)
- [Communication](#communication)
- [Installation](#installation)
- [Usage](#usage)
- [Credits](#credits)
- [License](#license)
## Requirements
- iOS 12.0+ / Mac OS X 10.13+ / tvOS 10.0+
- Xcode 10.0+
- Swift 4.0+
## Migration Guides
- [SnapKit 3.0 Migration Guide](Documentation/SnapKit%203.0%20Migration%20Guide.md)
## Communication
- If you **need help**, use [Stack Overflow](http://stackoverflow.com/questions/tagged/snapkit). (Tag 'snapkit')
- If you'd like to **ask a general question**, use [Stack Overflow](http://stackoverflow.com/questions/tagged/snapkit).
- If you **found a bug**, open an issue.
- If you **have a feature request**, open an issue.
- If you **want to contribute**, submit a pull request.
## Installation
### CocoaPods
[CocoaPods](http://cocoapods.org) is a dependency manager for Cocoa projects. You can install it with the following command:
```bash
$ gem install cocoapods
```
> CocoaPods 1.1.0+ is required to build SnapKit 4.0.0+.
To integrate SnapKit into your Xcode project using CocoaPods, specify it in your `Podfile`:
```ruby
source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '10.0'
use_frameworks!
target '<Your Target Name>' do
pod 'SnapKit', '~> 5.7.0'
end
```
Then, run the following command:
```bash
$ pod install
```
### Carthage
[Carthage](https://github.com/Carthage/Carthage) is a decentralized dependency manager that builds your dependencies and provides you with binary frameworks.
You can install Carthage with [Homebrew](http://brew.sh/) using the following command:
```bash
$ brew update
$ brew install carthage
```
To integrate SnapKit into your Xcode project using Carthage, specify it in your `Cartfile`:
```ogdl
github "SnapKit/SnapKit" ~> 5.0.0
```
Run `carthage update` to build the framework and drag the built `SnapKit.framework` into your Xcode project.
### Swift Package Manager
[Swift Package Manager](https://swift.org/package-manager/) is a tool for managing the distribution of Swift code. Its integrated with the Swift build system to automate the process of downloading, compiling, and linking dependencies.
> Xcode 11+ is required to build SnapKit using Swift Package Manager.
To integrate SnapKit into your Xcode project using Swift Package Manager, add it to the dependencies value of your `Package.swift`:
```swift
dependencies: [
.package(url: "https://github.com/SnapKit/SnapKit.git", .upToNextMajor(from: "5.0.1"))
]
```
### Manually
If you prefer not to use either of the aforementioned dependency managers, you can integrate SnapKit into your project manually.
---
## Usage
### Quick Start
```swift
import SnapKit
class MyViewController: UIViewController {
lazy var box = UIView()
override func viewDidLoad() {
super.viewDidLoad()
self.view.addSubview(box)
box.backgroundColor = .green
box.snp.makeConstraints { (make) -> Void in
make.width.height.equalTo(50)
make.center.equalTo(self.view)
}
}
}
```
### Playground
You can try SnapKit in Playground.
**Note:**
> To try SnapKit in playground, open `SnapKit.xcworkspace` and build SnapKit.framework for any simulator first.
### Resources
- [Documentation](https://snapkit.github.io/SnapKit/docs/)
- [F.A.Q.](https://snapkit.github.io/SnapKit/faq/)
## Credits
- Robert Payne ([@robertjpayne](https://twitter.com/robertjpayne))
- Many other contributors
## License
SnapKit is released under the MIT license. See LICENSE for details.
+341
View File
@@ -0,0 +1,341 @@
//
// SnapKit
//
// Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#if canImport(UIKit)
import UIKit
#else
import AppKit
#endif
public final class Constraint {
internal let sourceLocation: (String, UInt)
internal let label: String?
private let from: ConstraintItem
private let to: ConstraintItem
private let relation: ConstraintRelation
private let multiplier: ConstraintMultiplierTarget
private var constant: ConstraintConstantTarget {
didSet {
self.updateConstantAndPriorityIfNeeded()
}
}
private var priority: ConstraintPriorityTarget {
didSet {
self.updateConstantAndPriorityIfNeeded()
}
}
public var layoutConstraints: [LayoutConstraint]
public var isActive: Bool {
set {
if newValue {
activate()
}
else {
deactivate()
}
}
get {
for layoutConstraint in self.layoutConstraints {
if layoutConstraint.isActive {
return true
}
}
return false
}
}
// MARK: Initialization
internal init(from: ConstraintItem,
to: ConstraintItem,
relation: ConstraintRelation,
sourceLocation: (String, UInt),
label: String?,
multiplier: ConstraintMultiplierTarget,
constant: ConstraintConstantTarget,
priority: ConstraintPriorityTarget) {
self.from = from
self.to = to
self.relation = relation
self.sourceLocation = sourceLocation
self.label = label
self.multiplier = multiplier
self.constant = constant
self.priority = priority
self.layoutConstraints = []
// get attributes
let layoutFromAttributes = self.from.attributes.layoutAttributes
let layoutToAttributes = self.to.attributes.layoutAttributes
// get layout from
let layoutFrom = self.from.layoutConstraintItem!
// get relation
let layoutRelation = self.relation.layoutRelation
for layoutFromAttribute in layoutFromAttributes {
// get layout to attribute
let layoutToAttribute: LayoutAttribute
#if canImport(UIKit)
if layoutToAttributes.count > 0 {
if self.from.attributes == .edges && self.to.attributes == .margins {
switch layoutFromAttribute {
case .left:
layoutToAttribute = .leftMargin
case .right:
layoutToAttribute = .rightMargin
case .top:
layoutToAttribute = .topMargin
case .bottom:
layoutToAttribute = .bottomMargin
default:
fatalError()
}
} else if self.from.attributes == .margins && self.to.attributes == .edges {
switch layoutFromAttribute {
case .leftMargin:
layoutToAttribute = .left
case .rightMargin:
layoutToAttribute = .right
case .topMargin:
layoutToAttribute = .top
case .bottomMargin:
layoutToAttribute = .bottom
default:
fatalError()
}
} else if self.from.attributes == .directionalEdges && self.to.attributes == .directionalMargins {
switch layoutFromAttribute {
case .leading:
layoutToAttribute = .leadingMargin
case .trailing:
layoutToAttribute = .trailingMargin
case .top:
layoutToAttribute = .topMargin
case .bottom:
layoutToAttribute = .bottomMargin
default:
fatalError()
}
} else if self.from.attributes == .directionalMargins && self.to.attributes == .directionalEdges {
switch layoutFromAttribute {
case .leadingMargin:
layoutToAttribute = .leading
case .trailingMargin:
layoutToAttribute = .trailing
case .topMargin:
layoutToAttribute = .top
case .bottomMargin:
layoutToAttribute = .bottom
default:
fatalError()
}
} else if self.from.attributes == self.to.attributes {
layoutToAttribute = layoutFromAttribute
} else {
layoutToAttribute = layoutToAttributes[0]
}
} else {
if self.to.target == nil && (layoutFromAttribute == .centerX || layoutFromAttribute == .centerY) {
layoutToAttribute = layoutFromAttribute == .centerX ? .left : .top
} else {
layoutToAttribute = layoutFromAttribute
}
}
#else
if self.from.attributes == self.to.attributes {
layoutToAttribute = layoutFromAttribute
} else if layoutToAttributes.count > 0 {
layoutToAttribute = layoutToAttributes[0]
} else {
layoutToAttribute = layoutFromAttribute
}
#endif
// get layout constant
let layoutConstant: CGFloat = self.constant.constraintConstantTargetValueFor(layoutAttribute: layoutToAttribute)
// get layout to
var layoutTo: AnyObject? = self.to.target
// use superview if possible
if layoutTo == nil && layoutToAttribute != .width && layoutToAttribute != .height {
layoutTo = layoutFrom.superview
}
// create layout constraint
let layoutConstraint = LayoutConstraint(
item: layoutFrom,
attribute: layoutFromAttribute,
relatedBy: layoutRelation,
toItem: layoutTo,
attribute: layoutToAttribute,
multiplier: self.multiplier.constraintMultiplierTargetValue,
constant: layoutConstant
)
// set label
layoutConstraint.label = self.label
// set priority
layoutConstraint.priority = LayoutPriority(rawValue: self.priority.constraintPriorityTargetValue)
// set constraint
layoutConstraint.constraint = self
// append
self.layoutConstraints.append(layoutConstraint)
}
}
// MARK: Public
@available(*, deprecated, renamed:"activate()")
public func install() {
self.activate()
}
@available(*, deprecated, renamed:"deactivate()")
public func uninstall() {
self.deactivate()
}
public func activate() {
self.activateIfNeeded()
}
public func deactivate() {
self.deactivateIfNeeded()
}
@discardableResult
public func update(offset: ConstraintOffsetTarget) -> Constraint {
self.constant = offset.constraintOffsetTargetValue
return self
}
@discardableResult
public func update(inset: ConstraintInsetTarget) -> Constraint {
self.constant = inset.constraintInsetTargetValue
return self
}
#if canImport(UIKit)
@discardableResult
@available(iOS 11.0, tvOS 11.0, *)
public func update(inset: ConstraintDirectionalInsetTarget) -> Constraint {
self.constant = inset.constraintDirectionalInsetTargetValue
return self
}
#endif
@discardableResult
public func update(priority: ConstraintPriorityTarget) -> Constraint {
self.priority = priority.constraintPriorityTargetValue
return self
}
@discardableResult
public func update(priority: ConstraintPriority) -> Constraint {
self.priority = priority.value
return self
}
@available(*, deprecated, renamed:"update(offset:)")
public func updateOffset(amount: ConstraintOffsetTarget) -> Void { self.update(offset: amount) }
@available(*, deprecated, renamed:"update(inset:)")
public func updateInsets(amount: ConstraintInsetTarget) -> Void { self.update(inset: amount) }
@available(*, deprecated, renamed:"update(priority:)")
public func updatePriority(amount: ConstraintPriorityTarget) -> Void { self.update(priority: amount) }
@available(*, deprecated, message:"Use update(priority: ConstraintPriorityTarget) instead.")
public func updatePriorityRequired() -> Void {}
@available(*, deprecated, message:"Use update(priority: ConstraintPriorityTarget) instead.")
public func updatePriorityHigh() -> Void { fatalError("Must be implemented by Concrete subclass.") }
@available(*, deprecated, message:"Use update(priority: ConstraintPriorityTarget) instead.")
public func updatePriorityMedium() -> Void { fatalError("Must be implemented by Concrete subclass.") }
@available(*, deprecated, message:"Use update(priority: ConstraintPriorityTarget) instead.")
public func updatePriorityLow() -> Void { fatalError("Must be implemented by Concrete subclass.") }
// MARK: Internal
internal func updateConstantAndPriorityIfNeeded() {
for layoutConstraint in self.layoutConstraints {
let attribute = (layoutConstraint.secondAttribute == .notAnAttribute) ? layoutConstraint.firstAttribute : layoutConstraint.secondAttribute
layoutConstraint.constant = self.constant.constraintConstantTargetValueFor(layoutAttribute: attribute)
let requiredPriority = ConstraintPriority.required.value
if (layoutConstraint.priority.rawValue < requiredPriority), (self.priority.constraintPriorityTargetValue != requiredPriority) {
layoutConstraint.priority = LayoutPriority(rawValue: self.priority.constraintPriorityTargetValue)
}
}
}
internal func activateIfNeeded(updatingExisting: Bool = false) {
guard let item = self.from.layoutConstraintItem else {
print("WARNING: SnapKit failed to get from item from constraint. Activate will be a no-op.")
return
}
let layoutConstraints = self.layoutConstraints
if updatingExisting {
var existingLayoutConstraints: [LayoutConstraint] = []
for constraint in item.constraints {
existingLayoutConstraints += constraint.layoutConstraints
}
for layoutConstraint in layoutConstraints {
let existingLayoutConstraint = existingLayoutConstraints.first { $0 == layoutConstraint }
guard let updateLayoutConstraint = existingLayoutConstraint else {
fatalError("Updated constraint could not find existing matching constraint to update: \(layoutConstraint)")
}
let updateLayoutAttribute = (updateLayoutConstraint.secondAttribute == .notAnAttribute) ? updateLayoutConstraint.firstAttribute : updateLayoutConstraint.secondAttribute
updateLayoutConstraint.constant = self.constant.constraintConstantTargetValueFor(layoutAttribute: updateLayoutAttribute)
}
} else {
NSLayoutConstraint.activate(layoutConstraints)
item.add(constraints: [self])
}
}
internal func deactivateIfNeeded() {
guard let item = self.from.layoutConstraintItem else {
print("WARNING: SnapKit failed to get from item from constraint. Deactivate will be a no-op.")
return
}
let layoutConstraints = self.layoutConstraints
NSLayoutConstraint.deactivate(layoutConstraints)
item.remove(constraints: [self])
}
}
@@ -0,0 +1,203 @@
//
// SnapKit
//
// Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#if canImport(UIKit)
import UIKit
#else
import AppKit
#endif
internal struct ConstraintAttributes : OptionSet, ExpressibleByIntegerLiteral {
typealias IntegerLiteralType = UInt
internal init(rawValue: UInt) {
self.rawValue = rawValue
}
internal init(_ rawValue: UInt) {
self.init(rawValue: rawValue)
}
internal init(nilLiteral: ()) {
self.rawValue = 0
}
internal init(integerLiteral rawValue: IntegerLiteralType) {
self.init(rawValue: rawValue)
}
internal private(set) var rawValue: UInt
internal static var allZeros: ConstraintAttributes { return 0 }
internal static func convertFromNilLiteral() -> ConstraintAttributes { return 0 }
internal var boolValue: Bool { return self.rawValue != 0 }
internal func toRaw() -> UInt { return self.rawValue }
internal static func fromRaw(_ raw: UInt) -> ConstraintAttributes? { return self.init(raw) }
internal static func fromMask(_ raw: UInt) -> ConstraintAttributes { return self.init(raw) }
// normal
internal static let none: ConstraintAttributes = 0
internal static let left: ConstraintAttributes = ConstraintAttributes(UInt(1) << 0)
internal static let top: ConstraintAttributes = ConstraintAttributes(UInt(1) << 1)
internal static let right: ConstraintAttributes = ConstraintAttributes(UInt(1) << 2)
internal static let bottom: ConstraintAttributes = ConstraintAttributes(UInt(1) << 3)
internal static let leading: ConstraintAttributes = ConstraintAttributes(UInt(1) << 4)
internal static let trailing: ConstraintAttributes = ConstraintAttributes(UInt(1) << 5)
internal static let width: ConstraintAttributes = ConstraintAttributes(UInt(1) << 6)
internal static let height: ConstraintAttributes = ConstraintAttributes(UInt(1) << 7)
internal static let centerX: ConstraintAttributes = ConstraintAttributes(UInt(1) << 8)
internal static let centerY: ConstraintAttributes = ConstraintAttributes(UInt(1) << 9)
internal static let lastBaseline: ConstraintAttributes = ConstraintAttributes(UInt(1) << 10)
@available(iOS 8.0, OSX 10.11, *)
internal static let firstBaseline: ConstraintAttributes = ConstraintAttributes(UInt(1) << 11)
@available(iOS 8.0, *)
internal static let leftMargin: ConstraintAttributes = ConstraintAttributes(UInt(1) << 12)
@available(iOS 8.0, *)
internal static let rightMargin: ConstraintAttributes = ConstraintAttributes(UInt(1) << 13)
@available(iOS 8.0, *)
internal static let topMargin: ConstraintAttributes = ConstraintAttributes(UInt(1) << 14)
@available(iOS 8.0, *)
internal static let bottomMargin: ConstraintAttributes = ConstraintAttributes(UInt(1) << 15)
@available(iOS 8.0, *)
internal static let leadingMargin: ConstraintAttributes = ConstraintAttributes(UInt(1) << 16)
@available(iOS 8.0, *)
internal static let trailingMargin: ConstraintAttributes = ConstraintAttributes(UInt(1) << 17)
@available(iOS 8.0, *)
internal static let centerXWithinMargins: ConstraintAttributes = ConstraintAttributes(UInt(1) << 18)
@available(iOS 8.0, *)
internal static let centerYWithinMargins: ConstraintAttributes = ConstraintAttributes(UInt(1) << 19)
// aggregates
internal static let edges: ConstraintAttributes = [.horizontalEdges, .verticalEdges]
internal static let horizontalEdges: ConstraintAttributes = [.left, .right]
internal static let verticalEdges: ConstraintAttributes = [.top, .bottom]
internal static let directionalEdges: ConstraintAttributes = [.directionalHorizontalEdges, .directionalVerticalEdges]
internal static let directionalHorizontalEdges: ConstraintAttributes = [.leading, .trailing]
internal static let directionalVerticalEdges: ConstraintAttributes = [.top, .bottom]
internal static let size: ConstraintAttributes = [.width, .height]
internal static let center: ConstraintAttributes = [.centerX, .centerY]
@available(iOS 8.0, *)
internal static let margins: ConstraintAttributes = [.leftMargin, .topMargin, .rightMargin, .bottomMargin]
@available(iOS 8.0, *)
internal static let directionalMargins: ConstraintAttributes = [.leadingMargin, .topMargin, .trailingMargin, .bottomMargin]
@available(iOS 8.0, *)
internal static let centerWithinMargins: ConstraintAttributes = [.centerXWithinMargins, .centerYWithinMargins]
internal var layoutAttributes:[LayoutAttribute] {
var attrs = [LayoutAttribute]()
if (self.contains(ConstraintAttributes.left)) {
attrs.append(.left)
}
if (self.contains(ConstraintAttributes.top)) {
attrs.append(.top)
}
if (self.contains(ConstraintAttributes.right)) {
attrs.append(.right)
}
if (self.contains(ConstraintAttributes.bottom)) {
attrs.append(.bottom)
}
if (self.contains(ConstraintAttributes.leading)) {
attrs.append(.leading)
}
if (self.contains(ConstraintAttributes.trailing)) {
attrs.append(.trailing)
}
if (self.contains(ConstraintAttributes.width)) {
attrs.append(.width)
}
if (self.contains(ConstraintAttributes.height)) {
attrs.append(.height)
}
if (self.contains(ConstraintAttributes.centerX)) {
attrs.append(.centerX)
}
if (self.contains(ConstraintAttributes.centerY)) {
attrs.append(.centerY)
}
if (self.contains(ConstraintAttributes.lastBaseline)) {
attrs.append(.lastBaseline)
}
#if canImport(UIKit)
if (self.contains(ConstraintAttributes.firstBaseline)) {
attrs.append(.firstBaseline)
}
if (self.contains(ConstraintAttributes.leftMargin)) {
attrs.append(.leftMargin)
}
if (self.contains(ConstraintAttributes.rightMargin)) {
attrs.append(.rightMargin)
}
if (self.contains(ConstraintAttributes.topMargin)) {
attrs.append(.topMargin)
}
if (self.contains(ConstraintAttributes.bottomMargin)) {
attrs.append(.bottomMargin)
}
if (self.contains(ConstraintAttributes.leadingMargin)) {
attrs.append(.leadingMargin)
}
if (self.contains(ConstraintAttributes.trailingMargin)) {
attrs.append(.trailingMargin)
}
if (self.contains(ConstraintAttributes.centerXWithinMargins)) {
attrs.append(.centerXWithinMargins)
}
if (self.contains(ConstraintAttributes.centerYWithinMargins)) {
attrs.append(.centerYWithinMargins)
}
#endif
return attrs
}
}
internal func + (left: ConstraintAttributes, right: ConstraintAttributes) -> ConstraintAttributes {
return left.union(right)
}
internal func +=(left: inout ConstraintAttributes, right: ConstraintAttributes) {
left.formUnion(right)
}
internal func -=(left: inout ConstraintAttributes, right: ConstraintAttributes) {
left.subtract(right)
}
internal func ==(left: ConstraintAttributes, right: ConstraintAttributes) -> Bool {
return left.rawValue == right.rawValue
}
+37
View File
@@ -0,0 +1,37 @@
//
// SnapKit
//
// Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#if canImport(UIKit)
import UIKit
public typealias ConstraintInterfaceLayoutDirection = UIUserInterfaceLayoutDirection
#else
import AppKit
public typealias ConstraintInterfaceLayoutDirection = NSUserInterfaceLayoutDirection
#endif
public struct ConstraintConfig {
public static var interfaceLayoutDirection: ConstraintInterfaceLayoutDirection = .leftToRight
}
@@ -0,0 +1,213 @@
//
// SnapKit
//
// Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#if canImport(UIKit)
import UIKit
#else
import AppKit
#endif
public protocol ConstraintConstantTarget {
}
extension CGPoint: ConstraintConstantTarget {
}
extension CGSize: ConstraintConstantTarget {
}
extension ConstraintInsets: ConstraintConstantTarget {
}
#if canImport(UIKit)
@available(iOS 11.0, tvOS 11.0, *)
extension ConstraintDirectionalInsets: ConstraintConstantTarget {
}
#endif
extension ConstraintConstantTarget {
internal func constraintConstantTargetValueFor(layoutAttribute: LayoutAttribute) -> CGFloat {
if let value = self as? CGFloat {
return value
}
if let value = self as? Float {
return CGFloat(value)
}
if let value = self as? Double {
return CGFloat(value)
}
if let value = self as? Int {
return CGFloat(value)
}
if let value = self as? UInt {
return CGFloat(value)
}
if let value = self as? CGSize {
if layoutAttribute == .width {
return value.width
} else if layoutAttribute == .height {
return value.height
} else {
return 0.0
}
}
if let value = self as? CGPoint {
#if canImport(UIKit)
switch layoutAttribute {
case .left, .right, .leading, .trailing, .centerX, .leftMargin, .rightMargin, .leadingMargin, .trailingMargin, .centerXWithinMargins:
return value.x
case .top, .bottom, .centerY, .topMargin, .bottomMargin, .centerYWithinMargins, .lastBaseline, .firstBaseline:
return value.y
case .width, .height, .notAnAttribute:
return 0.0
#if swift(>=5.0)
@unknown default:
return 0.0
#endif
}
#else
switch layoutAttribute {
case .left, .right, .leading, .trailing, .centerX:
return value.x
case .top, .bottom, .centerY, .lastBaseline, .firstBaseline:
return value.y
case .width, .height, .notAnAttribute:
return 0.0
#if swift(>=5.0)
@unknown default:
return 0.0
#endif
}
#endif
}
if let value = self as? ConstraintInsets {
#if canImport(UIKit)
switch layoutAttribute {
case .left, .leftMargin:
return value.left
case .top, .topMargin, .firstBaseline:
return value.top
case .right, .rightMargin:
return -value.right
case .bottom, .bottomMargin, .lastBaseline:
return -value.bottom
case .leading, .leadingMargin:
return (ConstraintConfig.interfaceLayoutDirection == .leftToRight) ? value.left : value.right
case .trailing, .trailingMargin:
return (ConstraintConfig.interfaceLayoutDirection == .leftToRight) ? -value.right : -value.left
case .centerX, .centerXWithinMargins:
return (value.left - value.right) / 2
case .centerY, .centerYWithinMargins:
return (value.top - value.bottom) / 2
case .width:
return -(value.left + value.right)
case .height:
return -(value.top + value.bottom)
case .notAnAttribute:
return 0.0
#if swift(>=5.0)
@unknown default:
return 0.0
#endif
}
#else
switch layoutAttribute {
case .left:
return value.left
case .top, .firstBaseline:
return value.top
case .right:
return -value.right
case .bottom, .lastBaseline:
return -value.bottom
case .leading:
return (ConstraintConfig.interfaceLayoutDirection == .leftToRight) ? value.left : value.right
case .trailing:
return (ConstraintConfig.interfaceLayoutDirection == .leftToRight) ? -value.right : -value.left
case .centerX:
return (value.left - value.right) / 2
case .centerY:
return (value.top - value.bottom) / 2
case .width:
return -(value.left + value.right)
case .height:
return -(value.top + value.bottom)
case .notAnAttribute:
return 0.0
#if swift(>=5.0)
@unknown default:
return 0.0
#endif
}
#endif
}
#if canImport(UIKit)
if #available(iOS 11.0, tvOS 11.0, *), let value = self as? ConstraintDirectionalInsets {
switch layoutAttribute {
case .left, .leftMargin:
return (ConstraintConfig.interfaceLayoutDirection == .leftToRight) ? value.leading : value.trailing
case .top, .topMargin, .firstBaseline:
return value.top
case .right, .rightMargin:
return (ConstraintConfig.interfaceLayoutDirection == .leftToRight) ? -value.trailing : -value.leading
case .bottom, .bottomMargin, .lastBaseline:
return -value.bottom
case .leading, .leadingMargin:
return value.leading
case .trailing, .trailingMargin:
return -value.trailing
case .centerX, .centerXWithinMargins:
return (value.leading - value.trailing) / 2
case .centerY, .centerYWithinMargins:
return (value.top - value.bottom) / 2
case .width:
return -(value.leading + value.trailing)
case .height:
return -(value.top + value.bottom)
case .notAnAttribute:
return 0.0
#if swift(>=5.0)
@unknown default:
return 0.0
#else
default:
return 0.0
#endif
}
}
#endif
return 0.0
}
}
+209
View File
@@ -0,0 +1,209 @@
//
// SnapKit
//
// Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#if canImport(UIKit)
import UIKit
#else
import AppKit
#endif
public protocol ConstraintDSL {
var target: AnyObject? { get }
func setLabel(_ value: String?)
func label() -> String?
}
extension ConstraintDSL {
public func setLabel(_ value: String?) {
objc_setAssociatedObject(self.target as Any, &labelKey, value, .OBJC_ASSOCIATION_COPY_NONATOMIC)
}
public func label() -> String? {
return objc_getAssociatedObject(self.target as Any, &labelKey) as? String
}
}
private var labelKey: UInt8 = 0
public protocol ConstraintBasicAttributesDSL : ConstraintDSL {
}
extension ConstraintBasicAttributesDSL {
// MARK: Basics
public var left: ConstraintItem {
return ConstraintItem(target: self.target, attributes: ConstraintAttributes.left)
}
public var top: ConstraintItem {
return ConstraintItem(target: self.target, attributes: ConstraintAttributes.top)
}
public var right: ConstraintItem {
return ConstraintItem(target: self.target, attributes: ConstraintAttributes.right)
}
public var bottom: ConstraintItem {
return ConstraintItem(target: self.target, attributes: ConstraintAttributes.bottom)
}
public var leading: ConstraintItem {
return ConstraintItem(target: self.target, attributes: ConstraintAttributes.leading)
}
public var trailing: ConstraintItem {
return ConstraintItem(target: self.target, attributes: ConstraintAttributes.trailing)
}
public var width: ConstraintItem {
return ConstraintItem(target: self.target, attributes: ConstraintAttributes.width)
}
public var height: ConstraintItem {
return ConstraintItem(target: self.target, attributes: ConstraintAttributes.height)
}
public var centerX: ConstraintItem {
return ConstraintItem(target: self.target, attributes: ConstraintAttributes.centerX)
}
public var centerY: ConstraintItem {
return ConstraintItem(target: self.target, attributes: ConstraintAttributes.centerY)
}
public var edges: ConstraintItem {
return ConstraintItem(target: self.target, attributes: ConstraintAttributes.edges)
}
public var directionalEdges: ConstraintItem {
return ConstraintItem(target: self.target, attributes: ConstraintAttributes.directionalEdges)
}
public var horizontalEdges: ConstraintItem {
return ConstraintItem(target: self.target, attributes: ConstraintAttributes.horizontalEdges)
}
public var verticalEdges: ConstraintItem {
return ConstraintItem(target: self.target, attributes: ConstraintAttributes.verticalEdges)
}
public var directionalHorizontalEdges: ConstraintItem {
return ConstraintItem(target: self.target, attributes: ConstraintAttributes.directionalHorizontalEdges)
}
public var directionalVerticalEdges: ConstraintItem {
return ConstraintItem(target: self.target, attributes: ConstraintAttributes.directionalVerticalEdges)
}
public var size: ConstraintItem {
return ConstraintItem(target: self.target, attributes: ConstraintAttributes.size)
}
public var center: ConstraintItem {
return ConstraintItem(target: self.target, attributes: ConstraintAttributes.center)
}
}
public protocol ConstraintAttributesDSL : ConstraintBasicAttributesDSL {
}
extension ConstraintAttributesDSL {
// MARK: Baselines
@available(*, deprecated, renamed:"lastBaseline")
public var baseline: ConstraintItem {
return ConstraintItem(target: self.target, attributes: ConstraintAttributes.lastBaseline)
}
@available(iOS 8.0, OSX 10.11, *)
public var lastBaseline: ConstraintItem {
return ConstraintItem(target: self.target, attributes: ConstraintAttributes.lastBaseline)
}
@available(iOS 8.0, OSX 10.11, *)
public var firstBaseline: ConstraintItem {
return ConstraintItem(target: self.target, attributes: ConstraintAttributes.firstBaseline)
}
// MARK: Margins
@available(iOS 8.0, *)
public var leftMargin: ConstraintItem {
return ConstraintItem(target: self.target, attributes: ConstraintAttributes.leftMargin)
}
@available(iOS 8.0, *)
public var topMargin: ConstraintItem {
return ConstraintItem(target: self.target, attributes: ConstraintAttributes.topMargin)
}
@available(iOS 8.0, *)
public var rightMargin: ConstraintItem {
return ConstraintItem(target: self.target, attributes: ConstraintAttributes.rightMargin)
}
@available(iOS 8.0, *)
public var bottomMargin: ConstraintItem {
return ConstraintItem(target: self.target, attributes: ConstraintAttributes.bottomMargin)
}
@available(iOS 8.0, *)
public var leadingMargin: ConstraintItem {
return ConstraintItem(target: self.target, attributes: ConstraintAttributes.leadingMargin)
}
@available(iOS 8.0, *)
public var trailingMargin: ConstraintItem {
return ConstraintItem(target: self.target, attributes: ConstraintAttributes.trailingMargin)
}
@available(iOS 8.0, *)
public var centerXWithinMargins: ConstraintItem {
return ConstraintItem(target: self.target, attributes: ConstraintAttributes.centerXWithinMargins)
}
@available(iOS 8.0, *)
public var centerYWithinMargins: ConstraintItem {
return ConstraintItem(target: self.target, attributes: ConstraintAttributes.centerYWithinMargins)
}
@available(iOS 8.0, *)
public var margins: ConstraintItem {
return ConstraintItem(target: self.target, attributes: ConstraintAttributes.margins)
}
@available(iOS 8.0, *)
public var directionalMargins: ConstraintItem {
return ConstraintItem(target: self.target, attributes: ConstraintAttributes.directionalMargins)
}
@available(iOS 8.0, *)
public var centerWithinMargins: ConstraintItem {
return ConstraintItem(target: self.target, attributes: ConstraintAttributes.centerWithinMargins)
}
}
@@ -0,0 +1,69 @@
//
// SnapKit
//
// Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#if canImport(UIKit)
import UIKit
#else
import AppKit
#endif
public class ConstraintDescription {
internal let item: LayoutConstraintItem
internal var attributes: ConstraintAttributes
internal var relation: ConstraintRelation? = nil
internal var sourceLocation: (String, UInt)? = nil
internal var label: String? = nil
internal var related: ConstraintItem? = nil
internal var multiplier: ConstraintMultiplierTarget = 1.0
internal var constant: ConstraintConstantTarget = 0.0
internal var priority: ConstraintPriorityTarget = 1000.0
internal lazy var constraint: Constraint? = {
guard let relation = self.relation,
let related = self.related,
let sourceLocation = self.sourceLocation else {
return nil
}
let from = ConstraintItem(target: self.item, attributes: self.attributes)
return Constraint(
from: from,
to: related,
relation: relation,
sourceLocation: sourceLocation,
label: self.label,
multiplier: self.multiplier,
constant: self.constant,
priority: self.priority
)
}()
// MARK: Initialization
internal init(item: LayoutConstraintItem, attributes: ConstraintAttributes) {
self.item = item
self.attributes = attributes
}
}
@@ -0,0 +1,49 @@
//
// SnapKit
//
// Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#if canImport(UIKit)
import UIKit
#else
import AppKit
#endif
#if canImport(UIKit)
public protocol ConstraintDirectionalInsetTarget: ConstraintConstantTarget {
}
@available(iOS 11.0, tvOS 11.0, *)
extension ConstraintDirectionalInsets: ConstraintDirectionalInsetTarget {
}
extension ConstraintDirectionalInsetTarget {
@available(iOS 11.0, tvOS 11.0, *)
internal var constraintDirectionalInsetTargetValue: ConstraintDirectionalInsets {
if let amount = self as? ConstraintDirectionalInsets {
return amount
} else {
return ConstraintDirectionalInsets(top: 0, leading: 0, bottom: 0, trailing: 0)
}
}
}
#endif
@@ -0,0 +1,34 @@
//
// SnapKit
//
// Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#if canImport(UIKit)
import UIKit
#else
import AppKit
#endif
#if canImport(UIKit)
@available(iOS 11.0, tvOS 11.0, *)
public typealias ConstraintDirectionalInsets = NSDirectionalEdgeInsets
#endif
@@ -0,0 +1,72 @@
//
// SnapKit
//
// Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#if canImport(UIKit)
import UIKit
#else
import AppKit
#endif
public protocol ConstraintInsetTarget: ConstraintConstantTarget {
}
extension Int: ConstraintInsetTarget {
}
extension UInt: ConstraintInsetTarget {
}
extension Float: ConstraintInsetTarget {
}
extension Double: ConstraintInsetTarget {
}
extension CGFloat: ConstraintInsetTarget {
}
extension ConstraintInsets: ConstraintInsetTarget {
}
extension ConstraintInsetTarget {
internal var constraintInsetTargetValue: ConstraintInsets {
if let amount = self as? ConstraintInsets {
return amount
} else if let amount = self as? Float {
return ConstraintInsets(top: CGFloat(amount), left: CGFloat(amount), bottom: CGFloat(amount), right: CGFloat(amount))
} else if let amount = self as? Double {
return ConstraintInsets(top: CGFloat(amount), left: CGFloat(amount), bottom: CGFloat(amount), right: CGFloat(amount))
} else if let amount = self as? CGFloat {
return ConstraintInsets(top: amount, left: amount, bottom: amount, right: amount)
} else if let amount = self as? Int {
return ConstraintInsets(top: CGFloat(amount), left: CGFloat(amount), bottom: CGFloat(amount), right: CGFloat(amount))
} else if let amount = self as? UInt {
return ConstraintInsets(top: CGFloat(amount), left: CGFloat(amount), bottom: CGFloat(amount), right: CGFloat(amount))
} else {
return ConstraintInsets(top: 0, left: 0, bottom: 0, right: 0)
}
}
}
+35
View File
@@ -0,0 +1,35 @@
//
// SnapKit
//
// Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#if canImport(UIKit)
import UIKit
#else
import AppKit
#endif
#if canImport(UIKit)
public typealias ConstraintInsets = UIEdgeInsets
#else
public typealias ConstraintInsets = NSEdgeInsets
#endif
+61
View File
@@ -0,0 +1,61 @@
//
// SnapKit
//
// Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#if canImport(UIKit)
import UIKit
#else
import AppKit
#endif
public final class ConstraintItem {
internal weak var target: AnyObject?
internal let attributes: ConstraintAttributes
internal init(target: AnyObject?, attributes: ConstraintAttributes) {
self.target = target
self.attributes = attributes
}
internal var layoutConstraintItem: LayoutConstraintItem? {
return self.target as? LayoutConstraintItem
}
}
public func ==(lhs: ConstraintItem, rhs: ConstraintItem) -> Bool {
// pointer equality
guard lhs !== rhs else {
return true
}
// must both have valid targets and identical attributes
guard let target1 = lhs.target,
let target2 = rhs.target,
target1 === target2 && lhs.attributes == rhs.attributes else {
return false
}
return true
}
@@ -0,0 +1,36 @@
//
// SnapKit
//
// Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#if canImport(UIKit)
import UIKit
#endif
@available(iOS 9.0, OSX 10.11, *)
public extension ConstraintLayoutGuide {
var snp: ConstraintLayoutGuideDSL {
return ConstraintLayoutGuideDSL(guide: self)
}
}
@@ -0,0 +1,37 @@
//
// SnapKit
//
// Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#if canImport(UIKit)
import UIKit
#else
import AppKit
#endif
#if canImport(UIKit)
@available(iOS 9.0, *)
public typealias ConstraintLayoutGuide = UILayoutGuide
#else
@available(OSX 10.11, *)
public typealias ConstraintLayoutGuide = NSLayoutGuide
#endif
@@ -0,0 +1,66 @@
//
// SnapKit
//
// Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#if canImport(UIKit)
import UIKit
#else
import AppKit
#endif
@available(iOS 9.0, OSX 10.11, *)
public struct ConstraintLayoutGuideDSL: ConstraintAttributesDSL {
@discardableResult
public func prepareConstraints(_ closure: (_ make: ConstraintMaker) -> Void) -> [Constraint] {
return ConstraintMaker.prepareConstraints(item: self.guide, closure: closure)
}
public func makeConstraints(_ closure: (_ make: ConstraintMaker) -> Void) {
ConstraintMaker.makeConstraints(item: self.guide, closure: closure)
}
public func remakeConstraints(_ closure: (_ make: ConstraintMaker) -> Void) {
ConstraintMaker.remakeConstraints(item: self.guide, closure: closure)
}
public func updateConstraints(_ closure: (_ make: ConstraintMaker) -> Void) {
ConstraintMaker.updateConstraints(item: self.guide, closure: closure)
}
public func removeConstraints() {
ConstraintMaker.removeConstraints(item: self.guide)
}
public var target: AnyObject? {
return self.guide
}
internal let guide: ConstraintLayoutGuide
internal init(guide: ConstraintLayoutGuide) {
self.guide = guide
}
}
@@ -0,0 +1,36 @@
//
// SnapKit
//
// Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#if canImport(UIKit)
import UIKit
#else
import AppKit
#endif
#if canImport(UIKit)
@available(iOS 8.0, *)
public typealias ConstraintLayoutSupport = UILayoutSupport
#else
public class ConstraintLayoutSupport {}
#endif
@@ -0,0 +1,56 @@
//
// SnapKit
//
// Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#if canImport(UIKit)
import UIKit
#else
import AppKit
#endif
@available(iOS 8.0, *)
public struct ConstraintLayoutSupportDSL: ConstraintDSL {
public var target: AnyObject? {
return self.support
}
internal let support: ConstraintLayoutSupport
internal init(support: ConstraintLayoutSupport) {
self.support = support
}
public var top: ConstraintItem {
return ConstraintItem(target: self.target, attributes: ConstraintAttributes.top)
}
public var bottom: ConstraintItem {
return ConstraintItem(target: self.target, attributes: ConstraintAttributes.bottom)
}
public var height: ConstraintItem {
return ConstraintItem(target: self.target, attributes: ConstraintAttributes.height)
}
}
+224
View File
@@ -0,0 +1,224 @@
//
// SnapKit
//
// Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#if canImport(UIKit)
import UIKit
#else
import AppKit
#endif
public class ConstraintMaker {
public var left: ConstraintMakerExtendable {
return self.makeExtendableWithAttributes(.left)
}
public var top: ConstraintMakerExtendable {
return self.makeExtendableWithAttributes(.top)
}
public var bottom: ConstraintMakerExtendable {
return self.makeExtendableWithAttributes(.bottom)
}
public var right: ConstraintMakerExtendable {
return self.makeExtendableWithAttributes(.right)
}
public var leading: ConstraintMakerExtendable {
return self.makeExtendableWithAttributes(.leading)
}
public var trailing: ConstraintMakerExtendable {
return self.makeExtendableWithAttributes(.trailing)
}
public var width: ConstraintMakerExtendable {
return self.makeExtendableWithAttributes(.width)
}
public var height: ConstraintMakerExtendable {
return self.makeExtendableWithAttributes(.height)
}
public var centerX: ConstraintMakerExtendable {
return self.makeExtendableWithAttributes(.centerX)
}
public var centerY: ConstraintMakerExtendable {
return self.makeExtendableWithAttributes(.centerY)
}
@available(*, deprecated, renamed:"lastBaseline")
public var baseline: ConstraintMakerExtendable {
return self.makeExtendableWithAttributes(.lastBaseline)
}
public var lastBaseline: ConstraintMakerExtendable {
return self.makeExtendableWithAttributes(.lastBaseline)
}
@available(iOS 8.0, OSX 10.11, *)
public var firstBaseline: ConstraintMakerExtendable {
return self.makeExtendableWithAttributes(.firstBaseline)
}
@available(iOS 8.0, *)
public var leftMargin: ConstraintMakerExtendable {
return self.makeExtendableWithAttributes(.leftMargin)
}
@available(iOS 8.0, *)
public var rightMargin: ConstraintMakerExtendable {
return self.makeExtendableWithAttributes(.rightMargin)
}
@available(iOS 8.0, *)
public var topMargin: ConstraintMakerExtendable {
return self.makeExtendableWithAttributes(.topMargin)
}
@available(iOS 8.0, *)
public var bottomMargin: ConstraintMakerExtendable {
return self.makeExtendableWithAttributes(.bottomMargin)
}
@available(iOS 8.0, *)
public var leadingMargin: ConstraintMakerExtendable {
return self.makeExtendableWithAttributes(.leadingMargin)
}
@available(iOS 8.0, *)
public var trailingMargin: ConstraintMakerExtendable {
return self.makeExtendableWithAttributes(.trailingMargin)
}
@available(iOS 8.0, *)
public var centerXWithinMargins: ConstraintMakerExtendable {
return self.makeExtendableWithAttributes(.centerXWithinMargins)
}
@available(iOS 8.0, *)
public var centerYWithinMargins: ConstraintMakerExtendable {
return self.makeExtendableWithAttributes(.centerYWithinMargins)
}
public var edges: ConstraintMakerExtendable {
return self.makeExtendableWithAttributes(.edges)
}
public var horizontalEdges: ConstraintMakerExtendable {
return self.makeExtendableWithAttributes(.horizontalEdges)
}
public var verticalEdges: ConstraintMakerExtendable {
return self.makeExtendableWithAttributes(.verticalEdges)
}
public var directionalEdges: ConstraintMakerExtendable {
return self.makeExtendableWithAttributes(.directionalEdges)
}
public var directionalHorizontalEdges: ConstraintMakerExtendable {
return self.makeExtendableWithAttributes(.directionalHorizontalEdges)
}
public var directionalVerticalEdges: ConstraintMakerExtendable {
return self.makeExtendableWithAttributes(.directionalVerticalEdges)
}
public var size: ConstraintMakerExtendable {
return self.makeExtendableWithAttributes(.size)
}
public var center: ConstraintMakerExtendable {
return self.makeExtendableWithAttributes(.center)
}
@available(iOS 8.0, *)
public var margins: ConstraintMakerExtendable {
return self.makeExtendableWithAttributes(.margins)
}
@available(iOS 8.0, *)
public var directionalMargins: ConstraintMakerExtendable {
return self.makeExtendableWithAttributes(.directionalMargins)
}
@available(iOS 8.0, *)
public var centerWithinMargins: ConstraintMakerExtendable {
return self.makeExtendableWithAttributes(.centerWithinMargins)
}
public let item: LayoutConstraintItem
private var descriptions = [ConstraintDescription]()
internal init(item: LayoutConstraintItem) {
self.item = item
self.item.prepare()
}
internal func makeExtendableWithAttributes(_ attributes: ConstraintAttributes) -> ConstraintMakerExtendable {
let description = ConstraintDescription(item: self.item, attributes: attributes)
self.descriptions.append(description)
return ConstraintMakerExtendable(description)
}
internal static func prepareConstraints(item: LayoutConstraintItem, closure: (_ make: ConstraintMaker) -> Void) -> [Constraint] {
let maker = ConstraintMaker(item: item)
closure(maker)
var constraints: [Constraint] = []
for description in maker.descriptions {
guard let constraint = description.constraint else {
continue
}
constraints.append(constraint)
}
return constraints
}
internal static func makeConstraints(item: LayoutConstraintItem, closure: (_ make: ConstraintMaker) -> Void) {
let constraints = prepareConstraints(item: item, closure: closure)
for constraint in constraints {
constraint.activateIfNeeded(updatingExisting: false)
}
}
internal static func remakeConstraints(item: LayoutConstraintItem, closure: (_ make: ConstraintMaker) -> Void) {
self.removeConstraints(item: item)
self.makeConstraints(item: item, closure: closure)
}
internal static func updateConstraints(item: LayoutConstraintItem, closure: (_ make: ConstraintMaker) -> Void) {
guard item.constraints.count > 0 else {
self.makeConstraints(item: item, closure: closure)
return
}
let constraints = prepareConstraints(item: item, closure: closure)
for constraint in constraints {
constraint.activateIfNeeded(updatingExisting: true)
}
}
internal static func removeConstraints(item: LayoutConstraintItem) {
let constraints = item.constraints
for constraint in constraints {
constraint.deactivateIfNeeded()
}
}
}
@@ -0,0 +1,64 @@
//
// SnapKit
//
// Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#if canImport(UIKit)
import UIKit
#else
import AppKit
#endif
public class ConstraintMakerEditable: ConstraintMakerPrioritizable {
@discardableResult
public func multipliedBy(_ amount: ConstraintMultiplierTarget) -> ConstraintMakerEditable {
self.description.multiplier = amount
return self
}
@discardableResult
public func dividedBy(_ amount: ConstraintMultiplierTarget) -> ConstraintMakerEditable {
return self.multipliedBy(1.0 / amount.constraintMultiplierTargetValue)
}
@discardableResult
public func offset(_ amount: ConstraintOffsetTarget) -> ConstraintMakerEditable {
self.description.constant = amount.constraintOffsetTargetValue
return self
}
@discardableResult
public func inset(_ amount: ConstraintInsetTarget) -> ConstraintMakerEditable {
self.description.constant = amount.constraintInsetTargetValue
return self
}
#if canImport(UIKit)
@discardableResult
@available(iOS 11.0, tvOS 11.0, *)
public func inset(_ amount: ConstraintDirectionalInsetTarget) -> ConstraintMakerEditable {
self.description.constant = amount.constraintDirectionalInsetTargetValue
return self
}
#endif
}
@@ -0,0 +1,195 @@
//
// SnapKit
//
// Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#if canImport(UIKit)
import UIKit
#else
import AppKit
#endif
public class ConstraintMakerExtendable: ConstraintMakerRelatable {
public var left: ConstraintMakerExtendable {
self.description.attributes += .left
return self
}
public var top: ConstraintMakerExtendable {
self.description.attributes += .top
return self
}
public var bottom: ConstraintMakerExtendable {
self.description.attributes += .bottom
return self
}
public var right: ConstraintMakerExtendable {
self.description.attributes += .right
return self
}
public var leading: ConstraintMakerExtendable {
self.description.attributes += .leading
return self
}
public var trailing: ConstraintMakerExtendable {
self.description.attributes += .trailing
return self
}
public var width: ConstraintMakerExtendable {
self.description.attributes += .width
return self
}
public var height: ConstraintMakerExtendable {
self.description.attributes += .height
return self
}
public var centerX: ConstraintMakerExtendable {
self.description.attributes += .centerX
return self
}
public var centerY: ConstraintMakerExtendable {
self.description.attributes += .centerY
return self
}
@available(*, deprecated, renamed:"lastBaseline")
public var baseline: ConstraintMakerExtendable {
self.description.attributes += .lastBaseline
return self
}
public var lastBaseline: ConstraintMakerExtendable {
self.description.attributes += .lastBaseline
return self
}
@available(iOS 8.0, OSX 10.11, *)
public var firstBaseline: ConstraintMakerExtendable {
self.description.attributes += .firstBaseline
return self
}
@available(iOS 8.0, *)
public var leftMargin: ConstraintMakerExtendable {
self.description.attributes += .leftMargin
return self
}
@available(iOS 8.0, *)
public var rightMargin: ConstraintMakerExtendable {
self.description.attributes += .rightMargin
return self
}
@available(iOS 8.0, *)
public var topMargin: ConstraintMakerExtendable {
self.description.attributes += .topMargin
return self
}
@available(iOS 8.0, *)
public var bottomMargin: ConstraintMakerExtendable {
self.description.attributes += .bottomMargin
return self
}
@available(iOS 8.0, *)
public var leadingMargin: ConstraintMakerExtendable {
self.description.attributes += .leadingMargin
return self
}
@available(iOS 8.0, *)
public var trailingMargin: ConstraintMakerExtendable {
self.description.attributes += .trailingMargin
return self
}
@available(iOS 8.0, *)
public var centerXWithinMargins: ConstraintMakerExtendable {
self.description.attributes += .centerXWithinMargins
return self
}
@available(iOS 8.0, *)
public var centerYWithinMargins: ConstraintMakerExtendable {
self.description.attributes += .centerYWithinMargins
return self
}
public var edges: ConstraintMakerExtendable {
self.description.attributes += .edges
return self
}
public var horizontalEdges: ConstraintMakerExtendable {
self.description.attributes += .horizontalEdges
return self
}
public var verticalEdges: ConstraintMakerExtendable {
self.description.attributes += .verticalEdges
return self
}
public var directionalEdges: ConstraintMakerExtendable {
self.description.attributes += .directionalEdges
return self
}
public var directionalHorizontalEdges: ConstraintMakerExtendable {
self.description.attributes += .directionalHorizontalEdges
return self
}
public var directionalVerticalEdges: ConstraintMakerExtendable {
self.description.attributes += .directionalVerticalEdges
return self
}
public var size: ConstraintMakerExtendable {
self.description.attributes += .size
return self
}
@available(iOS 8.0, *)
public var margins: ConstraintMakerExtendable {
self.description.attributes += .margins
return self
}
@available(iOS 8.0, *)
public var directionalMargins: ConstraintMakerExtendable {
self.description.attributes += .directionalMargins
return self
}
@available(iOS 8.0, *)
public var centerWithinMargins: ConstraintMakerExtendable {
self.description.attributes += .centerWithinMargins
return self
}
}
@@ -0,0 +1,49 @@
//
// SnapKit
//
// Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#if canImport(UIKit)
import UIKit
#else
import AppKit
#endif
public class ConstraintMakerFinalizable {
internal let description: ConstraintDescription
internal init(_ description: ConstraintDescription) {
self.description = description
}
@discardableResult
public func labeled(_ label: String) -> ConstraintMakerFinalizable {
self.description.label = label
return self
}
public var constraint: Constraint {
return self.description.constraint!
}
}
@@ -0,0 +1,70 @@
//
// SnapKit
//
// Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#if canImport(UIKit)
import UIKit
#else
import AppKit
#endif
@available(*, deprecated, message:"Use ConstraintMakerPrioritizable instead.")
public typealias ConstraintMakerPriortizable = ConstraintMakerPrioritizable
public class ConstraintMakerPrioritizable: ConstraintMakerFinalizable {
@discardableResult
public func priority(_ amount: ConstraintPriority) -> ConstraintMakerFinalizable {
self.description.priority = amount.value
return self
}
@discardableResult
public func priority(_ amount: ConstraintPriorityTarget) -> ConstraintMakerFinalizable {
self.description.priority = amount
return self
}
@available(*, deprecated, message:"Use priority(.required) instead.")
@discardableResult
public func priorityRequired() -> ConstraintMakerFinalizable {
return self.priority(.required)
}
@available(*, deprecated, message:"Use priority(.high) instead.")
@discardableResult
public func priorityHigh() -> ConstraintMakerFinalizable {
return self.priority(.high)
}
@available(*, deprecated, message:"Use priority(.medium) instead.")
@discardableResult
public func priorityMedium() -> ConstraintMakerFinalizable {
return self.priority(.medium)
}
@available(*, deprecated, message:"Use priority(.low) instead.")
@discardableResult
public func priorityLow() -> ConstraintMakerFinalizable {
return self.priority(.low)
}
}
@@ -0,0 +1,57 @@
//
// SnapKit
//
// Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#if canImport(UIKit)
import UIKit
#else
import AppKit
#endif
extension ConstraintMakerRelatable {
@discardableResult
public func equalToSuperview<T: ConstraintRelatableTarget>(_ closure: (ConstraintView) -> T, _ file: String = #file, line: UInt = #line) -> ConstraintMakerEditable {
guard let other = self.description.item.superview else {
fatalError("Expected superview but found nil when attempting make constraint `equalToSuperview`.")
}
return self.relatedTo(closure(other), relation: .equal, file: file, line: line)
}
@discardableResult
public func lessThanOrEqualToSuperview<T: ConstraintRelatableTarget>(_ closure: (ConstraintView) -> T, _ file: String = #file, line: UInt = #line) -> ConstraintMakerEditable {
guard let other = self.description.item.superview else {
fatalError("Expected superview but found nil when attempting make constraint `lessThanOrEqualToSuperview`.")
}
return self.relatedTo(closure(other), relation: .lessThanOrEqual, file: file, line: line)
}
@discardableResult
public func greaterThanOrEqualToSuperview<T: ConstraintRelatableTarget>(_ closure: (ConstraintView) -> T, _ file: String = #file, line: UInt = #line) -> ConstraintMakerEditable {
guard let other = self.description.item.superview else {
fatalError("Expected superview but found nil when attempting make constraint `greaterThanOrEqualToSuperview`.")
}
return self.relatedTo(closure(other), relation: .greaterThanOrEqual, file: file, line: line)
}
}
@@ -0,0 +1,115 @@
//
// SnapKit
//
// Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#if canImport(UIKit)
import UIKit
#else
import AppKit
#endif
public class ConstraintMakerRelatable {
internal let description: ConstraintDescription
internal init(_ description: ConstraintDescription) {
self.description = description
}
internal func relatedTo(_ other: ConstraintRelatableTarget, relation: ConstraintRelation, file: String, line: UInt) -> ConstraintMakerEditable {
let related: ConstraintItem
let constant: ConstraintConstantTarget
if let other = other as? ConstraintItem {
guard other.attributes == ConstraintAttributes.none ||
other.attributes.layoutAttributes.count <= 1 ||
other.attributes.layoutAttributes == self.description.attributes.layoutAttributes ||
other.attributes == .edges && self.description.attributes == .margins ||
other.attributes == .margins && self.description.attributes == .edges ||
other.attributes == .directionalEdges && self.description.attributes == .directionalMargins ||
other.attributes == .directionalMargins && self.description.attributes == .directionalEdges else {
fatalError("Cannot constraint to multiple non identical attributes. (\(file), \(line))");
}
related = other
constant = 0.0
} else if let other = other as? ConstraintView {
related = ConstraintItem(target: other, attributes: ConstraintAttributes.none)
constant = 0.0
} else if let other = other as? ConstraintConstantTarget {
related = ConstraintItem(target: nil, attributes: ConstraintAttributes.none)
constant = other
} else if #available(iOS 9.0, OSX 10.11, *), let other = other as? ConstraintLayoutGuide {
related = ConstraintItem(target: other, attributes: ConstraintAttributes.none)
constant = 0.0
} else {
fatalError("Invalid constraint. (\(file), \(line))")
}
let editable = ConstraintMakerEditable(self.description)
editable.description.sourceLocation = (file, line)
editable.description.relation = relation
editable.description.related = related
editable.description.constant = constant
return editable
}
@discardableResult
public func equalTo(_ other: ConstraintRelatableTarget, _ file: String = #file, _ line: UInt = #line) -> ConstraintMakerEditable {
return self.relatedTo(other, relation: .equal, file: file, line: line)
}
@discardableResult
public func equalToSuperview(_ file: String = #file, _ line: UInt = #line) -> ConstraintMakerEditable {
guard let other = self.description.item.superview else {
fatalError("Expected superview but found nil when attempting make constraint `equalToSuperview`.")
}
return self.relatedTo(other, relation: .equal, file: file, line: line)
}
@discardableResult
public func lessThanOrEqualTo(_ other: ConstraintRelatableTarget, _ file: String = #file, _ line: UInt = #line) -> ConstraintMakerEditable {
return self.relatedTo(other, relation: .lessThanOrEqual, file: file, line: line)
}
@discardableResult
public func lessThanOrEqualToSuperview(_ file: String = #file, _ line: UInt = #line) -> ConstraintMakerEditable {
guard let other = self.description.item.superview else {
fatalError("Expected superview but found nil when attempting make constraint `lessThanOrEqualToSuperview`.")
}
return self.relatedTo(other, relation: .lessThanOrEqual, file: file, line: line)
}
@discardableResult
public func greaterThanOrEqualTo(_ other: ConstraintRelatableTarget, _ file: String = #file, line: UInt = #line) -> ConstraintMakerEditable {
return self.relatedTo(other, relation: .greaterThanOrEqual, file: file, line: line)
}
@discardableResult
public func greaterThanOrEqualToSuperview(_ file: String = #file, line: UInt = #line) -> ConstraintMakerEditable {
guard let other = self.description.item.superview else {
fatalError("Expected superview but found nil when attempting make constraint `greaterThanOrEqualToSuperview`.")
}
return self.relatedTo(other, relation: .greaterThanOrEqual, file: file, line: line)
}
}
@@ -0,0 +1,75 @@
//
// SnapKit
//
// Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#if canImport(UIKit)
import UIKit
#else
import AppKit
#endif
public protocol ConstraintMultiplierTarget {
var constraintMultiplierTargetValue: CGFloat { get }
}
extension Int: ConstraintMultiplierTarget {
public var constraintMultiplierTargetValue: CGFloat {
return CGFloat(self)
}
}
extension UInt: ConstraintMultiplierTarget {
public var constraintMultiplierTargetValue: CGFloat {
return CGFloat(self)
}
}
extension Float: ConstraintMultiplierTarget {
public var constraintMultiplierTargetValue: CGFloat {
return CGFloat(self)
}
}
extension Double: ConstraintMultiplierTarget {
public var constraintMultiplierTargetValue: CGFloat {
return CGFloat(self)
}
}
extension CGFloat: ConstraintMultiplierTarget {
public var constraintMultiplierTargetValue: CGFloat {
return self
}
}
@@ -0,0 +1,69 @@
//
// SnapKit
//
// Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#if canImport(UIKit)
import UIKit
#else
import AppKit
#endif
public protocol ConstraintOffsetTarget: ConstraintConstantTarget {
}
extension Int: ConstraintOffsetTarget {
}
extension UInt: ConstraintOffsetTarget {
}
extension Float: ConstraintOffsetTarget {
}
extension Double: ConstraintOffsetTarget {
}
extension CGFloat: ConstraintOffsetTarget {
}
extension ConstraintOffsetTarget {
internal var constraintOffsetTargetValue: CGFloat {
let offset: CGFloat
if let amount = self as? Float {
offset = CGFloat(amount)
} else if let amount = self as? Double {
offset = CGFloat(amount)
} else if let amount = self as? CGFloat {
offset = CGFloat(amount)
} else if let amount = self as? Int {
offset = CGFloat(amount)
} else if let amount = self as? UInt {
offset = CGFloat(amount)
} else {
offset = 0.0
}
return offset
}
}
@@ -0,0 +1,77 @@
//
// SnapKit
//
// Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#if canImport(UIKit)
import UIKit
#else
import AppKit
#endif
public struct ConstraintPriority : ExpressibleByFloatLiteral, Equatable, Strideable {
public typealias FloatLiteralType = Float
public let value: Float
public init(floatLiteral value: Float) {
self.value = value
}
public init(_ value: Float) {
self.value = value
}
public static var required: ConstraintPriority {
return 1000.0
}
public static var high: ConstraintPriority {
return 750.0
}
public static var medium: ConstraintPriority {
#if os(OSX)
return 501.0
#else
return 500.0
#endif
}
public static var low: ConstraintPriority {
return 250.0
}
public static func ==(lhs: ConstraintPriority, rhs: ConstraintPriority) -> Bool {
return lhs.value == rhs.value
}
// MARK: Strideable
public func advanced(by n: FloatLiteralType) -> ConstraintPriority {
return ConstraintPriority(floatLiteral: value + n)
}
public func distance(to other: ConstraintPriority) -> FloatLiteralType {
return other.value - value
}
}
@@ -0,0 +1,85 @@
//
// SnapKit
//
// Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#if canImport(UIKit)
import UIKit
#else
import AppKit
#endif
public protocol ConstraintPriorityTarget {
var constraintPriorityTargetValue: Float { get }
}
extension Int: ConstraintPriorityTarget {
public var constraintPriorityTargetValue: Float {
return Float(self)
}
}
extension UInt: ConstraintPriorityTarget {
public var constraintPriorityTargetValue: Float {
return Float(self)
}
}
extension Float: ConstraintPriorityTarget {
public var constraintPriorityTargetValue: Float {
return self
}
}
extension Double: ConstraintPriorityTarget {
public var constraintPriorityTargetValue: Float {
return Float(self)
}
}
extension CGFloat: ConstraintPriorityTarget {
public var constraintPriorityTargetValue: Float {
return Float(self)
}
}
#if canImport(UIKit)
extension UILayoutPriority: ConstraintPriorityTarget {
public var constraintPriorityTargetValue: Float {
return self.rawValue
}
}
#endif
@@ -0,0 +1,72 @@
//
// SnapKit
//
// Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#if canImport(UIKit)
import UIKit
#else
import AppKit
#endif
public protocol ConstraintRelatableTarget {
}
extension Int: ConstraintRelatableTarget {
}
extension UInt: ConstraintRelatableTarget {
}
extension Float: ConstraintRelatableTarget {
}
extension Double: ConstraintRelatableTarget {
}
extension CGFloat: ConstraintRelatableTarget {
}
extension CGSize: ConstraintRelatableTarget {
}
extension CGPoint: ConstraintRelatableTarget {
}
extension ConstraintInsets: ConstraintRelatableTarget {
}
#if canImport(UIKit)
@available(iOS 11.0, tvOS 11.0, *)
extension ConstraintDirectionalInsets: ConstraintRelatableTarget {
}
#endif
extension ConstraintItem: ConstraintRelatableTarget {
}
extension ConstraintView: ConstraintRelatableTarget {
}
@available(iOS 9.0, OSX 10.11, *)
extension ConstraintLayoutGuide: ConstraintRelatableTarget {
}
@@ -0,0 +1,48 @@
//
// SnapKit
//
// Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#if canImport(UIKit)
import UIKit
#else
import AppKit
#endif
internal enum ConstraintRelation : Int {
case equal = 1
case lessThanOrEqual
case greaterThanOrEqual
internal var layoutRelation: LayoutRelation {
get {
switch(self) {
case .equal:
return .equal
case .lessThanOrEqual:
return .lessThanOrEqual
case .greaterThanOrEqual:
return .greaterThanOrEqual
}
}
}
}
@@ -0,0 +1,152 @@
//
// SnapKit
//
// Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#if canImport(UIKit)
import UIKit
#else
import AppKit
#endif
public extension ConstraintView {
@available(*, deprecated, renamed:"snp.left")
var snp_left: ConstraintItem { return self.snp.left }
@available(*, deprecated, renamed:"snp.top")
var snp_top: ConstraintItem { return self.snp.top }
@available(*, deprecated, renamed:"snp.right")
var snp_right: ConstraintItem { return self.snp.right }
@available(*, deprecated, renamed:"snp.bottom")
var snp_bottom: ConstraintItem { return self.snp.bottom }
@available(*, deprecated, renamed:"snp.leading")
var snp_leading: ConstraintItem { return self.snp.leading }
@available(*, deprecated, renamed:"snp.trailing")
var snp_trailing: ConstraintItem { return self.snp.trailing }
@available(*, deprecated, renamed:"snp.width")
var snp_width: ConstraintItem { return self.snp.width }
@available(*, deprecated, renamed:"snp.height")
var snp_height: ConstraintItem { return self.snp.height }
@available(*, deprecated, renamed:"snp.centerX")
var snp_centerX: ConstraintItem { return self.snp.centerX }
@available(*, deprecated, renamed:"snp.centerY")
var snp_centerY: ConstraintItem { return self.snp.centerY }
@available(*, deprecated, renamed:"snp.baseline")
var snp_baseline: ConstraintItem { return self.snp.baseline }
@available(*, deprecated, renamed:"snp.lastBaseline")
@available(iOS 8.0, OSX 10.11, *)
var snp_lastBaseline: ConstraintItem { return self.snp.lastBaseline }
@available(iOS, deprecated, renamed:"snp.firstBaseline")
@available(iOS 8.0, OSX 10.11, *)
var snp_firstBaseline: ConstraintItem { return self.snp.firstBaseline }
@available(iOS, deprecated, renamed:"snp.leftMargin")
@available(iOS 8.0, *)
var snp_leftMargin: ConstraintItem { return self.snp.leftMargin }
@available(iOS, deprecated, renamed:"snp.topMargin")
@available(iOS 8.0, *)
var snp_topMargin: ConstraintItem { return self.snp.topMargin }
@available(iOS, deprecated, renamed:"snp.rightMargin")
@available(iOS 8.0, *)
var snp_rightMargin: ConstraintItem { return self.snp.rightMargin }
@available(iOS, deprecated, renamed:"snp.bottomMargin")
@available(iOS 8.0, *)
var snp_bottomMargin: ConstraintItem { return self.snp.bottomMargin }
@available(iOS, deprecated, renamed:"snp.leadingMargin")
@available(iOS 8.0, *)
var snp_leadingMargin: ConstraintItem { return self.snp.leadingMargin }
@available(iOS, deprecated, renamed:"snp.trailingMargin")
@available(iOS 8.0, *)
var snp_trailingMargin: ConstraintItem { return self.snp.trailingMargin }
@available(iOS, deprecated, renamed:"snp.centerXWithinMargins")
@available(iOS 8.0, *)
var snp_centerXWithinMargins: ConstraintItem { return self.snp.centerXWithinMargins }
@available(iOS, deprecated, renamed:"snp.centerYWithinMargins")
@available(iOS 8.0, *)
var snp_centerYWithinMargins: ConstraintItem { return self.snp.centerYWithinMargins }
@available(*, deprecated, renamed:"snp.edges")
var snp_edges: ConstraintItem { return self.snp.edges }
@available(*, deprecated, renamed:"snp.size")
var snp_size: ConstraintItem { return self.snp.size }
@available(*, deprecated, renamed:"snp.center")
var snp_center: ConstraintItem { return self.snp.center }
@available(iOS, deprecated, renamed:"snp.margins")
@available(iOS 8.0, *)
var snp_margins: ConstraintItem { return self.snp.margins }
@available(iOS, deprecated, renamed:"snp.centerWithinMargins")
@available(iOS 8.0, *)
var snp_centerWithinMargins: ConstraintItem { return self.snp.centerWithinMargins }
@available(*, deprecated, renamed:"snp.prepareConstraints(_:)")
func snp_prepareConstraints(_ closure: (_ make: ConstraintMaker) -> Void) -> [Constraint] {
return self.snp.prepareConstraints(closure)
}
@available(*, deprecated, renamed:"snp.makeConstraints(_:)")
func snp_makeConstraints(_ closure: (_ make: ConstraintMaker) -> Void) {
self.snp.makeConstraints(closure)
}
@available(*, deprecated, renamed:"snp.remakeConstraints(_:)")
func snp_remakeConstraints(_ closure: (_ make: ConstraintMaker) -> Void) {
self.snp.remakeConstraints(closure)
}
@available(*, deprecated, renamed:"snp.updateConstraints(_:)")
func snp_updateConstraints(_ closure: (_ make: ConstraintMaker) -> Void) {
self.snp.updateConstraints(closure)
}
@available(*, deprecated, renamed:"snp.removeConstraints()")
func snp_removeConstraints() {
self.snp.removeConstraints()
}
var snp: ConstraintViewDSL {
return ConstraintViewDSL(view: self)
}
}
+35
View File
@@ -0,0 +1,35 @@
//
// SnapKit
//
// Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#if canImport(UIKit)
import UIKit
#else
import AppKit
#endif
#if canImport(UIKit)
public typealias ConstraintView = UIView
#else
public typealias ConstraintView = NSView
#endif
+101
View File
@@ -0,0 +1,101 @@
//
// SnapKit
//
// Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#if canImport(UIKit)
import UIKit
#else
import AppKit
#endif
public struct ConstraintViewDSL: ConstraintAttributesDSL {
@discardableResult
public func prepareConstraints(_ closure: (_ make: ConstraintMaker) -> Void) -> [Constraint] {
return ConstraintMaker.prepareConstraints(item: self.view, closure: closure)
}
public func makeConstraints(_ closure: (_ make: ConstraintMaker) -> Void) {
ConstraintMaker.makeConstraints(item: self.view, closure: closure)
}
public func remakeConstraints(_ closure: (_ make: ConstraintMaker) -> Void) {
ConstraintMaker.remakeConstraints(item: self.view, closure: closure)
}
public func updateConstraints(_ closure: (_ make: ConstraintMaker) -> Void) {
ConstraintMaker.updateConstraints(item: self.view, closure: closure)
}
public func removeConstraints() {
ConstraintMaker.removeConstraints(item: self.view)
}
public var contentHuggingHorizontalPriority: Float {
get {
return self.view.contentHuggingPriority(for: .horizontal).rawValue
}
nonmutating set {
self.view.setContentHuggingPriority(LayoutPriority(rawValue: newValue), for: .horizontal)
}
}
public var contentHuggingVerticalPriority: Float {
get {
return self.view.contentHuggingPriority(for: .vertical).rawValue
}
nonmutating set {
self.view.setContentHuggingPriority(LayoutPriority(rawValue: newValue), for: .vertical)
}
}
public var contentCompressionResistanceHorizontalPriority: Float {
get {
return self.view.contentCompressionResistancePriority(for: .horizontal).rawValue
}
nonmutating set {
self.view.setContentCompressionResistancePriority(LayoutPriority(rawValue: newValue), for: .horizontal)
}
}
public var contentCompressionResistanceVerticalPriority: Float {
get {
return self.view.contentCompressionResistancePriority(for: .vertical).rawValue
}
nonmutating set {
self.view.setContentCompressionResistancePriority(LayoutPriority(rawValue: newValue), for: .vertical)
}
}
public var target: AnyObject? {
return self.view
}
internal let view: ConstraintView
internal init(view: ConstraintView) {
self.view = view
}
}
+169
View File
@@ -0,0 +1,169 @@
//
// SnapKit
//
// Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#if canImport(UIKit)
import UIKit
#else
import AppKit
#endif
public extension LayoutConstraint {
override var description: String {
var description = "<"
description += descriptionForObject(self)
if let firstItem = conditionalOptional(from: self.firstItem) {
description += " \(descriptionForObject(firstItem))"
}
if self.firstAttribute != .notAnAttribute {
description += ".\(descriptionForAttribute(self.firstAttribute))"
}
description += " \(descriptionForRelation(self.relation))"
if let secondItem = self.secondItem {
description += " \(descriptionForObject(secondItem))"
}
if self.secondAttribute != .notAnAttribute {
description += ".\(descriptionForAttribute(self.secondAttribute))"
}
if self.multiplier != 1.0 {
description += " * \(self.multiplier)"
}
if self.secondAttribute == .notAnAttribute {
description += " \(self.constant)"
} else {
if self.constant > 0.0 {
description += " + \(self.constant)"
} else if self.constant < 0.0 {
description += " - \(abs(self.constant))"
}
}
if self.priority.rawValue != 1000.0 {
description += " ^\(self.priority)"
}
description += ">"
return description
}
}
private func descriptionForRelation(_ relation: LayoutRelation) -> String {
switch relation {
case .equal: return "=="
case .greaterThanOrEqual: return ">="
case .lessThanOrEqual: return "<="
#if swift(>=5.0)
@unknown default: return "unknown"
#endif
}
}
private func descriptionForAttribute(_ attribute: LayoutAttribute) -> String {
#if canImport(UIKit)
switch attribute {
case .notAnAttribute: return "notAnAttribute"
case .top: return "top"
case .left: return "left"
case .bottom: return "bottom"
case .right: return "right"
case .leading: return "leading"
case .trailing: return "trailing"
case .width: return "width"
case .height: return "height"
case .centerX: return "centerX"
case .centerY: return "centerY"
case .lastBaseline: return "lastBaseline"
case .firstBaseline: return "firstBaseline"
case .topMargin: return "topMargin"
case .leftMargin: return "leftMargin"
case .bottomMargin: return "bottomMargin"
case .rightMargin: return "rightMargin"
case .leadingMargin: return "leadingMargin"
case .trailingMargin: return "trailingMargin"
case .centerXWithinMargins: return "centerXWithinMargins"
case .centerYWithinMargins: return "centerYWithinMargins"
#if swift(>=5.0)
@unknown default: return "unknown"
#endif
}
#else
switch attribute {
case .notAnAttribute: return "notAnAttribute"
case .top: return "top"
case .left: return "left"
case .bottom: return "bottom"
case .right: return "right"
case .leading: return "leading"
case .trailing: return "trailing"
case .width: return "width"
case .height: return "height"
case .centerX: return "centerX"
case .centerY: return "centerY"
case .lastBaseline: return "lastBaseline"
case .firstBaseline: return "firstBaseline"
#if swift(>=5.0)
@unknown default: return "unknown"
#endif
}
#endif
}
private func conditionalOptional<T>(from object: Optional<T>) -> Optional<T> {
return object
}
private func conditionalOptional<T>(from object: T) -> Optional<T> {
return Optional.some(object)
}
private func descriptionForObject(_ object: AnyObject) -> String {
let pointerDescription = String(format: "%p", UInt(bitPattern: ObjectIdentifier(object)))
var desc = ""
desc += type(of: object).description()
if let object = object as? ConstraintView {
desc += ":\(object.snp.label() ?? pointerDescription)"
} else if let object = object as? LayoutConstraint {
desc += ":\(object.label ?? pointerDescription)"
} else {
desc += ":\(pointerDescription)"
}
if let object = object as? LayoutConstraint, let file = object.constraint?.sourceLocation.0, let line = object.constraint?.sourceLocation.1 {
desc += "@\((file as NSString).lastPathComponent)#\(line)"
}
desc += ""
return desc
}
+61
View File
@@ -0,0 +1,61 @@
//
// SnapKit
//
// Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#if canImport(UIKit)
import UIKit
#else
import AppKit
#endif
public class LayoutConstraint : NSLayoutConstraint {
public var label: String? {
get {
return self.identifier
}
set {
self.identifier = newValue
}
}
internal weak var constraint: Constraint? = nil
}
internal func ==(lhs: LayoutConstraint, rhs: LayoutConstraint) -> Bool {
// If firstItem or secondItem on either constraint has a dangling pointer
// this comparison can cause a crash. The solution for this is to ensure
// your layout code hold strong references to things like Views, LayoutGuides
// and LayoutAnchors as SnapKit will not keep strong references to any of these.
guard lhs.firstAttribute == rhs.firstAttribute &&
lhs.secondAttribute == rhs.secondAttribute &&
lhs.relation == rhs.relation &&
lhs.priority == rhs.priority &&
lhs.multiplier == rhs.multiplier &&
lhs.secondItem === rhs.secondItem &&
lhs.firstItem === rhs.firstItem else {
return false
}
return true
}
@@ -0,0 +1,93 @@
//
// SnapKit
//
// Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#if canImport(UIKit)
import UIKit
#else
import AppKit
#endif
public protocol LayoutConstraintItem: AnyObject {
}
@available(iOS 9.0, OSX 10.11, *)
extension ConstraintLayoutGuide : LayoutConstraintItem {
}
extension ConstraintView : LayoutConstraintItem {
}
extension LayoutConstraintItem {
internal func prepare() {
if let view = self as? ConstraintView {
view.translatesAutoresizingMaskIntoConstraints = false
}
}
internal var superview: ConstraintView? {
if let view = self as? ConstraintView {
return view.superview
}
if #available(iOS 9.0, OSX 10.11, *), let guide = self as? ConstraintLayoutGuide {
return guide.owningView
}
return nil
}
internal var constraints: [Constraint] {
return self.constraintsSet.allObjects as! [Constraint]
}
internal func add(constraints: [Constraint]) {
let constraintsSet = self.constraintsSet
for constraint in constraints {
constraintsSet.add(constraint)
}
}
internal func remove(constraints: [Constraint]) {
let constraintsSet = self.constraintsSet
for constraint in constraints {
constraintsSet.remove(constraint)
}
}
private var constraintsSet: NSMutableSet {
let constraintsSet: NSMutableSet
if let existing = objc_getAssociatedObject(self, &constraintsKey) as? NSMutableSet {
constraintsSet = existing
} else {
constraintsSet = NSMutableSet()
objc_setAssociatedObject(self, &constraintsKey, constraintsSet, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
return constraintsSet
}
}
private var constraintsKey: UInt8 = 0
+14
View File
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSPrivacyTracking</key>
<false/>
<key>NSPrivacyAccessedAPITypes</key>
<array/>
<key>NSPrivacyCollectedDataTypes</key>
<array/>
<key>NSPrivacyTrackingDomains</key>
<array/>
</dict>
</plist>
+42
View File
@@ -0,0 +1,42 @@
//
// SnapKit
//
// Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
import Foundation
#if canImport(UIKit)
import UIKit
#if swift(>=4.2)
typealias LayoutRelation = NSLayoutConstraint.Relation
typealias LayoutAttribute = NSLayoutConstraint.Attribute
#else
typealias LayoutRelation = NSLayoutRelation
typealias LayoutAttribute = NSLayoutAttribute
#endif
typealias LayoutPriority = UILayoutPriority
#else
import AppKit
typealias LayoutRelation = NSLayoutConstraint.Relation
typealias LayoutAttribute = NSLayoutConstraint.Attribute
typealias LayoutPriority = NSLayoutConstraint.Priority
#endif
@@ -0,0 +1,36 @@
//
// SnapKit
//
// Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#if canImport(UIKit)
import UIKit
#endif
@available(iOS 8.0, *)
public extension ConstraintLayoutSupport {
var snp: ConstraintLayoutSupportDSL {
return ConstraintLayoutSupportDSL(support: self)
}
}
@@ -53,6 +53,29 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
## SnapKit
Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
## ZIPFoundation ## ZIPFoundation
MIT License MIT License
@@ -76,6 +76,35 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
<key>Type</key> <key>Type</key>
<string>PSGroupSpecifier</string> <string>PSGroupSpecifier</string>
</dict> </dict>
<dict>
<key>FooterText</key>
<string>Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
</string>
<key>License</key>
<string>MIT</string>
<key>Title</key>
<string>SnapKit</string>
<key>Type</key>
<string>PSGroupSpecifier</string>
</dict>
<dict> <dict>
<key>FooterText</key> <key>FooterText</key>
<string>MIT License <string>MIT License
@@ -1,5 +1,7 @@
${PODS_ROOT}/Target Support Files/Pods-ReadViewDemo/Pods-ReadViewDemo-frameworks.sh ${PODS_ROOT}/Target Support Files/Pods-ReadViewDemo/Pods-ReadViewDemo-frameworks.sh
${BUILT_PRODUCTS_DIR}/DTCoreText/DTCoreText.framework ${BUILT_PRODUCTS_DIR}/DTCoreText/DTCoreText.framework
${BUILT_PRODUCTS_DIR}/DTFoundation/DTFoundation.framework ${BUILT_PRODUCTS_DIR}/DTFoundation/DTFoundation.framework
${BUILT_PRODUCTS_DIR}/RDReaderView/RDReaderView.framework ${BUILT_PRODUCTS_DIR}/RDEpubReaderView/RDEpubReaderView.framework
${BUILT_PRODUCTS_DIR}/RDPDFReaderView/RDPDFReaderView.framework
${BUILT_PRODUCTS_DIR}/SnapKit/SnapKit.framework
${BUILT_PRODUCTS_DIR}/ZIPFoundation/ZIPFoundation.framework ${BUILT_PRODUCTS_DIR}/ZIPFoundation/ZIPFoundation.framework
@@ -1,4 +1,6 @@
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/DTCoreText.framework ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/DTCoreText.framework
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/DTFoundation.framework ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/DTFoundation.framework
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RDReaderView.framework ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RDEpubReaderView.framework
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RDPDFReaderView.framework
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SnapKit.framework
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/ZIPFoundation.framework ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/ZIPFoundation.framework
@@ -1,5 +1,7 @@
${PODS_ROOT}/Target Support Files/Pods-ReadViewDemo/Pods-ReadViewDemo-frameworks.sh ${PODS_ROOT}/Target Support Files/Pods-ReadViewDemo/Pods-ReadViewDemo-frameworks.sh
${BUILT_PRODUCTS_DIR}/DTCoreText/DTCoreText.framework ${BUILT_PRODUCTS_DIR}/DTCoreText/DTCoreText.framework
${BUILT_PRODUCTS_DIR}/DTFoundation/DTFoundation.framework ${BUILT_PRODUCTS_DIR}/DTFoundation/DTFoundation.framework
${BUILT_PRODUCTS_DIR}/RDReaderView/RDReaderView.framework ${BUILT_PRODUCTS_DIR}/RDEpubReaderView/RDEpubReaderView.framework
${BUILT_PRODUCTS_DIR}/RDPDFReaderView/RDPDFReaderView.framework
${BUILT_PRODUCTS_DIR}/SnapKit/SnapKit.framework
${BUILT_PRODUCTS_DIR}/ZIPFoundation/ZIPFoundation.framework ${BUILT_PRODUCTS_DIR}/ZIPFoundation/ZIPFoundation.framework
@@ -1,4 +1,6 @@
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/DTCoreText.framework ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/DTCoreText.framework
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/DTFoundation.framework ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/DTFoundation.framework
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RDReaderView.framework ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RDEpubReaderView.framework
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RDPDFReaderView.framework
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SnapKit.framework
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/ZIPFoundation.framework ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/ZIPFoundation.framework
@@ -178,13 +178,17 @@ code_sign_if_enabled() {
if [[ "$CONFIGURATION" == "Debug" ]]; then if [[ "$CONFIGURATION" == "Debug" ]]; then
install_framework "${BUILT_PRODUCTS_DIR}/DTCoreText/DTCoreText.framework" install_framework "${BUILT_PRODUCTS_DIR}/DTCoreText/DTCoreText.framework"
install_framework "${BUILT_PRODUCTS_DIR}/DTFoundation/DTFoundation.framework" install_framework "${BUILT_PRODUCTS_DIR}/DTFoundation/DTFoundation.framework"
install_framework "${BUILT_PRODUCTS_DIR}/RDReaderView/RDReaderView.framework" install_framework "${BUILT_PRODUCTS_DIR}/RDEpubReaderView/RDEpubReaderView.framework"
install_framework "${BUILT_PRODUCTS_DIR}/RDPDFReaderView/RDPDFReaderView.framework"
install_framework "${BUILT_PRODUCTS_DIR}/SnapKit/SnapKit.framework"
install_framework "${BUILT_PRODUCTS_DIR}/ZIPFoundation/ZIPFoundation.framework" install_framework "${BUILT_PRODUCTS_DIR}/ZIPFoundation/ZIPFoundation.framework"
fi fi
if [[ "$CONFIGURATION" == "Release" ]]; then if [[ "$CONFIGURATION" == "Release" ]]; then
install_framework "${BUILT_PRODUCTS_DIR}/DTCoreText/DTCoreText.framework" install_framework "${BUILT_PRODUCTS_DIR}/DTCoreText/DTCoreText.framework"
install_framework "${BUILT_PRODUCTS_DIR}/DTFoundation/DTFoundation.framework" install_framework "${BUILT_PRODUCTS_DIR}/DTFoundation/DTFoundation.framework"
install_framework "${BUILT_PRODUCTS_DIR}/RDReaderView/RDReaderView.framework" install_framework "${BUILT_PRODUCTS_DIR}/RDEpubReaderView/RDEpubReaderView.framework"
install_framework "${BUILT_PRODUCTS_DIR}/RDPDFReaderView/RDPDFReaderView.framework"
install_framework "${BUILT_PRODUCTS_DIR}/SnapKit/SnapKit.framework"
install_framework "${BUILT_PRODUCTS_DIR}/ZIPFoundation/ZIPFoundation.framework" install_framework "${BUILT_PRODUCTS_DIR}/ZIPFoundation/ZIPFoundation.framework"
fi fi
if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then
@@ -0,0 +1,2 @@
${PODS_ROOT}/Target Support Files/Pods-ReadViewDemo/Pods-ReadViewDemo-resources.sh
${PODS_CONFIGURATION_BUILD_DIR}/RDEpubReaderView/RDEpubReaderViewAssets.bundle
@@ -0,0 +1 @@
${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RDEpubReaderViewAssets.bundle
@@ -0,0 +1,2 @@
${PODS_ROOT}/Target Support Files/Pods-ReadViewDemo/Pods-ReadViewDemo-resources.sh
${PODS_CONFIGURATION_BUILD_DIR}/RDEpubReaderView/RDEpubReaderViewAssets.bundle
@@ -0,0 +1 @@
${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RDEpubReaderViewAssets.bundle
@@ -0,0 +1,129 @@
#!/bin/sh
set -e
set -u
set -o pipefail
function on_error {
echo "$(realpath -mq "${0}"):$1: error: Unexpected failure"
}
trap 'on_error $LINENO' ERR
if [ -z ${UNLOCALIZED_RESOURCES_FOLDER_PATH+x} ]; then
# If UNLOCALIZED_RESOURCES_FOLDER_PATH is not set, then there's nowhere for us to copy
# resources to, so exit 0 (signalling the script phase was successful).
exit 0
fi
mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt
> "$RESOURCES_TO_COPY"
XCASSET_FILES=()
# This protects against multiple targets copying the same framework dependency at the same time. The solution
# was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html
RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????")
case "${TARGETED_DEVICE_FAMILY:-}" in
1,2)
TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone"
;;
1)
TARGET_DEVICE_ARGS="--target-device iphone"
;;
2)
TARGET_DEVICE_ARGS="--target-device ipad"
;;
3)
TARGET_DEVICE_ARGS="--target-device tv"
;;
4)
TARGET_DEVICE_ARGS="--target-device watch"
;;
*)
TARGET_DEVICE_ARGS="--target-device mac"
;;
esac
install_resource()
{
if [[ "$1" = /* ]] ; then
RESOURCE_PATH="$1"
else
RESOURCE_PATH="${PODS_ROOT}/$1"
fi
if [[ ! -e "$RESOURCE_PATH" ]] ; then
cat << EOM
error: Resource "$RESOURCE_PATH" not found. Run 'pod install' to update the copy resources script.
EOM
exit 1
fi
case $RESOURCE_PATH in
*.storyboard)
echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true
ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS}
;;
*.xib)
echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true
ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS}
;;
*.framework)
echo "mkdir -p ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true
mkdir -p "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" $RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true
rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
;;
*.xcdatamodel)
echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH"`.mom\"" || true
xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodel`.mom"
;;
*.xcdatamodeld)
echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd\"" || true
xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd"
;;
*.xcmappingmodel)
echo "xcrun mapc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm\"" || true
xcrun mapc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm"
;;
*.xcassets)
ABSOLUTE_XCASSET_FILE="$RESOURCE_PATH"
XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE")
;;
*)
echo "$RESOURCE_PATH" || true
echo "$RESOURCE_PATH" >> "$RESOURCES_TO_COPY"
;;
esac
}
if [[ "$CONFIGURATION" == "Debug" ]]; then
install_resource "${PODS_CONFIGURATION_BUILD_DIR}/RDEpubReaderView/RDEpubReaderViewAssets.bundle"
fi
if [[ "$CONFIGURATION" == "Release" ]]; then
install_resource "${PODS_CONFIGURATION_BUILD_DIR}/RDEpubReaderView/RDEpubReaderViewAssets.bundle"
fi
mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then
mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
fi
rm -f "$RESOURCES_TO_COPY"
if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "${XCASSET_FILES:-}" ]
then
# Find all other xcassets (this unfortunately includes those of path pods and other targets).
OTHER_XCASSETS=$(find -L "$PWD" -iname "*.xcassets" -type d)
while read line; do
if [[ $line != "${PODS_ROOT}*" ]]; then
XCASSET_FILES+=("$line")
fi
done <<<"$OTHER_XCASSETS"
if [ -z ${ASSETCATALOG_COMPILER_APPICON_NAME+x} ]; then
printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
else
printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" --app-icon "${ASSETCATALOG_COMPILER_APPICON_NAME}" --output-partial-info-plist "${TARGET_TEMP_DIR}/assetcatalog_generated_info_cocoapods.plist"
fi
fi
@@ -1,12 +1,12 @@
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/DTCoreText" "${PODS_CONFIGURATION_BUILD_DIR}/DTFoundation" "${PODS_CONFIGURATION_BUILD_DIR}/RDReaderView" "${PODS_CONFIGURATION_BUILD_DIR}/ZIPFoundation" FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/DTCoreText" "${PODS_CONFIGURATION_BUILD_DIR}/DTFoundation" "${PODS_CONFIGURATION_BUILD_DIR}/RDEpubReaderView" "${PODS_CONFIGURATION_BUILD_DIR}/RDPDFReaderView" "${PODS_CONFIGURATION_BUILD_DIR}/SnapKit" "${PODS_CONFIGURATION_BUILD_DIR}/ZIPFoundation"
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/DTCoreText/DTCoreText.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/DTFoundation/DTFoundation.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/RDReaderView/RDReaderView.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/ZIPFoundation/ZIPFoundation.framework/Headers" "$(SDKROOT)/usr/include/libxml2" HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/DTCoreText/DTCoreText.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/DTFoundation/DTFoundation.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/RDEpubReaderView/RDEpubReaderView.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/RDPDFReaderView/RDPDFReaderView.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SnapKit/SnapKit.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/ZIPFoundation/ZIPFoundation.framework/Headers" "$(SDKROOT)/usr/include/libxml2"
LD_RUNPATH_SEARCH_PATHS = $(inherited) /usr/lib/swift '@executable_path/Frameworks' '@loader_path/Frameworks' LD_RUNPATH_SEARCH_PATHS = $(inherited) /usr/lib/swift '@executable_path/Frameworks' '@loader_path/Frameworks'
LIBRARY_SEARCH_PATHS = $(inherited) "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift LIBRARY_SEARCH_PATHS = $(inherited) "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift $(SDKROOT)/usr/lib/swift
OTHER_LDFLAGS = $(inherited) -l"xml2" -framework "CoreGraphics" -framework "CoreText" -framework "DTCoreText" -framework "DTFoundation" -framework "ImageIO" -framework "MediaPlayer" -framework "QuartzCore" -framework "RDReaderView" -framework "ZIPFoundation" OTHER_LDFLAGS = $(inherited) -l"swiftCoreGraphics" -l"xml2" -framework "CoreGraphics" -framework "CoreImage" -framework "CoreText" -framework "DTCoreText" -framework "DTFoundation" -framework "ImageIO" -framework "MediaPlayer" -framework "PDFKit" -framework "QuartzCore" -framework "RDEpubReaderView" -framework "RDPDFReaderView" -framework "SnapKit" -framework "Vision" -framework "ZIPFoundation"
OTHER_MODULE_VERIFIER_FLAGS = $(inherited) "-F${PODS_CONFIGURATION_BUILD_DIR}/DTCoreText" "-F${PODS_CONFIGURATION_BUILD_DIR}/DTFoundation" "-F${PODS_CONFIGURATION_BUILD_DIR}/RDReaderView" "-F${PODS_CONFIGURATION_BUILD_DIR}/ZIPFoundation" OTHER_MODULE_VERIFIER_FLAGS = $(inherited) "-F${PODS_CONFIGURATION_BUILD_DIR}/DTCoreText" "-F${PODS_CONFIGURATION_BUILD_DIR}/DTFoundation" "-F${PODS_CONFIGURATION_BUILD_DIR}/RDEpubReaderView" "-F${PODS_CONFIGURATION_BUILD_DIR}/RDPDFReaderView" "-F${PODS_CONFIGURATION_BUILD_DIR}/SnapKit" "-F${PODS_CONFIGURATION_BUILD_DIR}/ZIPFoundation"
OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS
PODS_BUILD_DIR = ${BUILD_DIR} PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
@@ -1,12 +1,12 @@
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/DTCoreText" "${PODS_CONFIGURATION_BUILD_DIR}/DTFoundation" "${PODS_CONFIGURATION_BUILD_DIR}/RDReaderView" "${PODS_CONFIGURATION_BUILD_DIR}/ZIPFoundation" FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/DTCoreText" "${PODS_CONFIGURATION_BUILD_DIR}/DTFoundation" "${PODS_CONFIGURATION_BUILD_DIR}/RDEpubReaderView" "${PODS_CONFIGURATION_BUILD_DIR}/RDPDFReaderView" "${PODS_CONFIGURATION_BUILD_DIR}/SnapKit" "${PODS_CONFIGURATION_BUILD_DIR}/ZIPFoundation"
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/DTCoreText/DTCoreText.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/DTFoundation/DTFoundation.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/RDReaderView/RDReaderView.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/ZIPFoundation/ZIPFoundation.framework/Headers" "$(SDKROOT)/usr/include/libxml2" HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/DTCoreText/DTCoreText.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/DTFoundation/DTFoundation.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/RDEpubReaderView/RDEpubReaderView.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/RDPDFReaderView/RDPDFReaderView.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SnapKit/SnapKit.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/ZIPFoundation/ZIPFoundation.framework/Headers" "$(SDKROOT)/usr/include/libxml2"
LD_RUNPATH_SEARCH_PATHS = $(inherited) /usr/lib/swift '@executable_path/Frameworks' '@loader_path/Frameworks' LD_RUNPATH_SEARCH_PATHS = $(inherited) /usr/lib/swift '@executable_path/Frameworks' '@loader_path/Frameworks'
LIBRARY_SEARCH_PATHS = $(inherited) "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift LIBRARY_SEARCH_PATHS = $(inherited) "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift $(SDKROOT)/usr/lib/swift
OTHER_LDFLAGS = $(inherited) -l"xml2" -framework "CoreGraphics" -framework "CoreText" -framework "DTCoreText" -framework "DTFoundation" -framework "ImageIO" -framework "MediaPlayer" -framework "QuartzCore" -framework "RDReaderView" -framework "ZIPFoundation" OTHER_LDFLAGS = $(inherited) -l"swiftCoreGraphics" -l"xml2" -framework "CoreGraphics" -framework "CoreImage" -framework "CoreText" -framework "DTCoreText" -framework "DTFoundation" -framework "ImageIO" -framework "MediaPlayer" -framework "PDFKit" -framework "QuartzCore" -framework "RDEpubReaderView" -framework "RDPDFReaderView" -framework "SnapKit" -framework "Vision" -framework "ZIPFoundation"
OTHER_MODULE_VERIFIER_FLAGS = $(inherited) "-F${PODS_CONFIGURATION_BUILD_DIR}/DTCoreText" "-F${PODS_CONFIGURATION_BUILD_DIR}/DTFoundation" "-F${PODS_CONFIGURATION_BUILD_DIR}/RDReaderView" "-F${PODS_CONFIGURATION_BUILD_DIR}/ZIPFoundation" OTHER_MODULE_VERIFIER_FLAGS = $(inherited) "-F${PODS_CONFIGURATION_BUILD_DIR}/DTCoreText" "-F${PODS_CONFIGURATION_BUILD_DIR}/DTFoundation" "-F${PODS_CONFIGURATION_BUILD_DIR}/RDEpubReaderView" "-F${PODS_CONFIGURATION_BUILD_DIR}/RDPDFReaderView" "-F${PODS_CONFIGURATION_BUILD_DIR}/SnapKit" "-F${PODS_CONFIGURATION_BUILD_DIR}/ZIPFoundation"
OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS
PODS_BUILD_DIR = ${BUILD_DIR} PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>${PODS_DEVELOPMENT_LANGUAGE}</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIdentifier</key>
<string>${PRODUCT_BUNDLE_IDENTIFIER}</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>${PRODUCT_NAME}</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>0.0.2</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>${CURRENT_PROJECT_VERSION}</string>
<key>NSPrincipalClass</key>
<string></string>
</dict>
</plist>
@@ -0,0 +1,5 @@
#import <Foundation/Foundation.h>
@interface PodsDummy_RDEpubReaderView : NSObject
@end
@implementation PodsDummy_RDEpubReaderView
@end
@@ -0,0 +1,16 @@
#ifdef __OBJC__
#import <UIKit/UIKit.h>
#else
#ifndef FOUNDATION_EXPORT
#if defined(__cplusplus)
#define FOUNDATION_EXPORT extern "C"
#else
#define FOUNDATION_EXPORT extern
#endif
#endif
#endif
FOUNDATION_EXPORT double RDEpubReaderViewVersionNumber;
FOUNDATION_EXPORT const unsigned char RDEpubReaderViewVersionString[];
@@ -1,15 +1,15 @@
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/RDReaderView CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/RDEpubReaderView
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/DTCoreText" "${PODS_CONFIGURATION_BUILD_DIR}/DTFoundation" "${PODS_CONFIGURATION_BUILD_DIR}/ZIPFoundation" FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/DTCoreText" "${PODS_CONFIGURATION_BUILD_DIR}/DTFoundation" "${PODS_CONFIGURATION_BUILD_DIR}/SnapKit" "${PODS_CONFIGURATION_BUILD_DIR}/ZIPFoundation"
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
LIBRARY_SEARCH_PATHS = $(inherited) "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift LIBRARY_SEARCH_PATHS = $(inherited) "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift
OTHER_LDFLAGS = $(inherited) -framework "CoreGraphics" -framework "CoreText" -framework "DTCoreText" -framework "ImageIO" -framework "MediaPlayer" -framework "QuartzCore" -framework "ZIPFoundation" OTHER_LDFLAGS = $(inherited) -framework "CoreGraphics" -framework "CoreText" -framework "DTCoreText" -framework "ImageIO" -framework "MediaPlayer" -framework "QuartzCore" -framework "SnapKit" -framework "ZIPFoundation"
OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS
PODS_BUILD_DIR = ${BUILD_DIR} PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_DEVELOPMENT_LANGUAGE = ${DEVELOPMENT_LANGUAGE} PODS_DEVELOPMENT_LANGUAGE = ${DEVELOPMENT_LANGUAGE}
PODS_ROOT = ${SRCROOT} PODS_ROOT = ${SRCROOT}
PODS_TARGET_SRCROOT = ${PODS_ROOT}/../.. PODS_TARGET_SRCROOT = ${PODS_ROOT}/../../Sources/RDEpubReaderView
PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates
PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
SKIP_INSTALL = YES SKIP_INSTALL = YES
@@ -0,0 +1,6 @@
framework module RDEpubReaderView {
umbrella header "RDEpubReaderView-umbrella.h"
export *
module * { export * }
}
@@ -1,15 +1,15 @@
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/RDReaderView CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/RDEpubReaderView
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/DTCoreText" "${PODS_CONFIGURATION_BUILD_DIR}/DTFoundation" "${PODS_CONFIGURATION_BUILD_DIR}/ZIPFoundation" FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/DTCoreText" "${PODS_CONFIGURATION_BUILD_DIR}/DTFoundation" "${PODS_CONFIGURATION_BUILD_DIR}/SnapKit" "${PODS_CONFIGURATION_BUILD_DIR}/ZIPFoundation"
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
LIBRARY_SEARCH_PATHS = $(inherited) "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift LIBRARY_SEARCH_PATHS = $(inherited) "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift
OTHER_LDFLAGS = $(inherited) -framework "CoreGraphics" -framework "CoreText" -framework "DTCoreText" -framework "ImageIO" -framework "MediaPlayer" -framework "QuartzCore" -framework "ZIPFoundation" OTHER_LDFLAGS = $(inherited) -framework "CoreGraphics" -framework "CoreText" -framework "DTCoreText" -framework "ImageIO" -framework "MediaPlayer" -framework "QuartzCore" -framework "SnapKit" -framework "ZIPFoundation"
OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS
PODS_BUILD_DIR = ${BUILD_DIR} PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_DEVELOPMENT_LANGUAGE = ${DEVELOPMENT_LANGUAGE} PODS_DEVELOPMENT_LANGUAGE = ${DEVELOPMENT_LANGUAGE}
PODS_ROOT = ${SRCROOT} PODS_ROOT = ${SRCROOT}
PODS_TARGET_SRCROOT = ${PODS_ROOT}/../.. PODS_TARGET_SRCROOT = ${PODS_ROOT}/../../Sources/RDEpubReaderView
PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates
PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
SKIP_INSTALL = YES SKIP_INSTALL = YES
@@ -13,7 +13,7 @@
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>BNDL</string> <string>BNDL</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>0.0.1</string> <string>0.0.2</string>
<key>CFBundleSignature</key> <key>CFBundleSignature</key>
<string>????</string> <string>????</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
@@ -0,0 +1,5 @@
#import <Foundation/Foundation.h>
@interface PodsDummy_RDPDFReaderView : NSObject
@end
@implementation PodsDummy_RDPDFReaderView
@end
@@ -0,0 +1,12 @@
#ifdef __OBJC__
#import <UIKit/UIKit.h>
#else
#ifndef FOUNDATION_EXPORT
#if defined(__cplusplus)
#define FOUNDATION_EXPORT extern "C"
#else
#define FOUNDATION_EXPORT extern
#endif
#endif
#endif
@@ -0,0 +1,16 @@
#ifdef __OBJC__
#import <UIKit/UIKit.h>
#else
#ifndef FOUNDATION_EXPORT
#if defined(__cplusplus)
#define FOUNDATION_EXPORT extern "C"
#else
#define FOUNDATION_EXPORT extern
#endif
#endif
#endif
FOUNDATION_EXPORT double RDPDFReaderViewVersionNumber;
FOUNDATION_EXPORT const unsigned char RDPDFReaderViewVersionString[];
@@ -0,0 +1,16 @@
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/RDPDFReaderView
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/SnapKit"
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
LIBRARY_SEARCH_PATHS = $(inherited) "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift
OTHER_LDFLAGS = $(inherited) -framework "CoreImage" -framework "PDFKit" -framework "SnapKit" -framework "Vision"
OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_DEVELOPMENT_LANGUAGE = ${DEVELOPMENT_LANGUAGE}
PODS_ROOT = ${SRCROOT}
PODS_TARGET_SRCROOT = ${PODS_ROOT}/../../Sources/RDPDFReaderView
PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates
PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
SKIP_INSTALL = YES
USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES
@@ -0,0 +1,6 @@
framework module RDPDFReaderView {
umbrella header "RDPDFReaderView-umbrella.h"
export *
module * { export * }
}
@@ -0,0 +1,16 @@
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/RDPDFReaderView
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/SnapKit"
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
LIBRARY_SEARCH_PATHS = $(inherited) "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift
OTHER_LDFLAGS = $(inherited) -framework "CoreImage" -framework "PDFKit" -framework "SnapKit" -framework "Vision"
OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_DEVELOPMENT_LANGUAGE = ${DEVELOPMENT_LANGUAGE}
PODS_ROOT = ${SRCROOT}
PODS_TARGET_SRCROOT = ${PODS_ROOT}/../../Sources/RDPDFReaderView
PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates
PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
SKIP_INSTALL = YES
USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES
@@ -1,5 +0,0 @@
#import <Foundation/Foundation.h>
@interface PodsDummy_RDReaderView : NSObject
@end
@implementation PodsDummy_RDReaderView
@end
@@ -1,6 +0,0 @@
framework module RDReaderView {
umbrella header "RDReaderView-umbrella.h"
export *
module * { export * }
}

Some files were not shown because too many files have changed in this diff Show More