feat: chapter runtime refactoring and related updates
- Refactor chapter runtime: replace window coordinator/snapshot with warmup orchestrator - Update EPUB core: parser, reading session, JS bridge, navigator layout - Update reader controller: data source, location resolution, persistence - Update chapter runtime: data cache, loader, runtime store, disk cache, warmup orchestrator - Remove deprecated navigation state machine and pagination state - Update text rendering: book cache, HTML normalizer - Update UI: text content view, dark image adjuster, text selection controller - Update settings and reader configuration - Add CODE_REVIEW.md and AUDIT_FINAL.md documentation - Update pod dependencies (remove SSAlertSwift, SnapKit) - Update podspec and pod configuration files Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
b8aa10c535
commit
22e7e44220
151
CODE_REVIEW.md
Normal file
151
CODE_REVIEW.md
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
# ReadViewSDK 代码审查报告
|
||||||
|
|
||||||
|
> 审查日期:2026-06-26
|
||||||
|
> 审查范围:Sources/RDReaderView 全部源码
|
||||||
|
> 审查方法:逐文件阅读 + 交叉验证 + 线程模型分析
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ 确认成立的问题
|
||||||
|
|
||||||
|
### P1-1 WKURLSchemeHandler 同步文件 I/O 阻塞调用线程
|
||||||
|
|
||||||
|
**文件**: `EPUBCore/RDEPUBResourceURLSchemeHandler.swift:51-174`
|
||||||
|
|
||||||
|
**现象**: `webView(_:start:)` 回调中,所有文件读取均同步执行:
|
||||||
|
|
||||||
|
- `respondWithInMemoryData`(第119行)使用 `Data(contentsOf:)` 全量读入 ≤512KB 的文件
|
||||||
|
- `respondWithStreaming`(第143行)在 `while true` 循环中同步读 64KB 块并逐块回调 `urlSchemeTask.didReceive(data)`
|
||||||
|
|
||||||
|
Apple 未保证 WKURLSchemeHandler 回调线程为主线程,实际在 WKWebView 内部队列上执行。同步阻塞该线程会:
|
||||||
|
|
||||||
|
- 若在主线程:直接造成 UI 卡顿/ANR
|
||||||
|
- 若在 WKWebView 内部队列:阻塞资源加载管线,影响渲染时序
|
||||||
|
|
||||||
|
**建议**: 将文件读取移至后台队列,通过 `DispatchQueue.main.async` 或回调队列回传 `didReceive`/`didFinish`。流式场景可改为分批异步读取。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### P1-2 EPUB 解压缓存目录无清理策略
|
||||||
|
|
||||||
|
**文件**: `EPUBCore/RDEPUBParser+Archive.swift:79-90`
|
||||||
|
|
||||||
|
**现象**: `temporaryExtractionDirectory(for:)` 按 `(slug)-(fileSize)-(modificationTimestamp)` 生成缓存目录,存放在 `Caches/ssreaderview-epub/`。全局搜索确认不存在任何清理逻辑——无 LRU 淘汰、无总量上限、无 `removeItem` 调用指向该目录。
|
||||||
|
|
||||||
|
一本 50MB 的 EPUB 解压后约 100-200MB,用户打开多本书后缓存目录无限增长。
|
||||||
|
|
||||||
|
**建议**: 添加缓存清理策略:
|
||||||
|
1. 提供 `clearCache()` 公开方法供宿主 App 在低存储时调用
|
||||||
|
2. 每次打开新书时按访问时间淘汰超出上限的旧缓存
|
||||||
|
3. 或采用 `FileManager.default.urls(for: .cachesDirectory:)` 依赖系统自动清理
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### P1-3 搜索在主线程同步执行
|
||||||
|
|
||||||
|
**文件**: `EPUBUI/ReaderController/RDEPUBReaderSearchCoordinator.swift:15-36`
|
||||||
|
|
||||||
|
**现象**: `search(keyword:)` 直接调用 `resolvedSearchMatches(for:)`,该方法遍历整本书的所有 spine 条目执行 HTML→文本转换或章节同步加载 + NSRange 搜索,全程在主线程完成。
|
||||||
|
|
||||||
|
对于大型 EPUB(数百章):
|
||||||
|
- UI 完全冻结直到搜索完成
|
||||||
|
- 用户无法取消或看到进度
|
||||||
|
- 可能触发 iOS 看门狗终止(0x8badf00d)
|
||||||
|
|
||||||
|
`resolvedOnDemandSearchMatches(for:)`(第125行)更严重:它同步加载每个章节(`loadChapterSynchronouslyForMigration`),整本书搜索意味着逐章同步加载。
|
||||||
|
|
||||||
|
**建议**: 将搜索逻辑移至后台队列,对大型书籍实施分批搜索 + 渐进结果回调,并在 UI 层添加搜索进度指示和取消能力。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### P2-1 ZIP 无效条目导致整本 EPUB 打开失败
|
||||||
|
|
||||||
|
**文件**: `EPUBCore/RDEPUBParser+Archive.swift:41-44`
|
||||||
|
|
||||||
|
**现象**: 解压循环中,若 `validatedExtractionDestination` 返回 `nil`(条目路径验证失败),直接 `throw` 终止整本书的解析。实际 EPUB 中常包含无害条目(macOS `__MACOSX/` 目录、`.DS_Store`、Thumbs.db 等),这些不应阻止打开。
|
||||||
|
|
||||||
|
```swift
|
||||||
|
for entry in archive {
|
||||||
|
guard let destinationURL = validatedExtractionDestination(for: entry.path, ...) else {
|
||||||
|
throw RDEPUBParserError.invalidArchiveEntryPath(entry.path) // 应改为 continue
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**建议**: 将验证失败的条目改为 `continue` + 日志警告,并增加对已知无害条目路径的跳过逻辑。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### P2-2 javaScriptStringLiteral 实现依赖隐式假设
|
||||||
|
|
||||||
|
**文件**: `EPUBCore/RDEPUBJavaScriptBridge.swift:169-174`
|
||||||
|
|
||||||
|
**现象**: `javaScriptStringLiteral` 将值包装进单元素数组做 JSON 编码,再暴力去掉 `[` 和 `]`:
|
||||||
|
|
||||||
|
```swift
|
||||||
|
private static func javaScriptStringLiteral(_ value: String?) -> String {
|
||||||
|
guard let value else { return "null" }
|
||||||
|
return jsonString(from: [value], fallback: "[null]")
|
||||||
|
.replacingOccurrences(of: "[", with: "")
|
||||||
|
.replacingOccurrences(of: "]", with: "")
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
这不是 XSS/注入问题(`JSONSerialization` 已正确转义引号/反斜杠等危险字符),但该方法依赖"值不含 `[` 或 `]`"的隐式假设。当前调用场景传入的是 CSS 颜色值(如 `#FFFFFF`、`rgba(...)`),不含方括号,所以实际安全。但未来如果有人将包含方括号的值传入,输出会被静默破坏。
|
||||||
|
|
||||||
|
**建议**: 改为更直观的安全编码方式,如直接对单值做 `JSONSerialization` 后去掉两端引号,或添加文档注释明确标注此方法的约束前提。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### P3-1 后台线程同步 hop 主线程获取布局信息
|
||||||
|
|
||||||
|
**文件**: `EPUBUI/ReaderController/RDEPUBReaderContext.swift:160-186`
|
||||||
|
|
||||||
|
**现象**: `currentTextPageSize()` 在非主线程且缓存不可用时,通过 `DispatchQueue.main.sync` 回到主线程获取布局信息。调用方包括 `RDEPUBMetadataParseWorker`(后台初始化,第72行)和 `RDEPUBChapterLoader`(`chapterLoadQueue.async`,第298行)。
|
||||||
|
|
||||||
|
```swift
|
||||||
|
func currentTextPageSize() -> CGSize {
|
||||||
|
if Thread.isMainThread {
|
||||||
|
// 主线程路径 — 安全
|
||||||
|
...
|
||||||
|
} else if let lastTextPaginationPageSize, ... {
|
||||||
|
// 缓存路径 — 无需 hop
|
||||||
|
return lastTextPaginationPageSize
|
||||||
|
} else {
|
||||||
|
let mainThreadSize = DispatchQueue.main.sync { [weak self] in
|
||||||
|
self?.currentTextPageSize() ?? .zero
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`Thread.isMainThread` 保护使得从主线程调用不会死锁。实际风险是**时序耦合**:后台任务阻塞等待主线程布局信息,如果主线程正在忙于 UI 操作(如翻页动画),后台线程会被卡住直到主线程空闲。这是一个线程模型/可维护性问题,而非可直接触发的死锁。
|
||||||
|
|
||||||
|
**建议**: 将布局参数作为初始化参数传入后台任务,而非在后台线程通过 `DispatchQueue.main.sync` 获取。这样后台线程完全自主,不依赖主线程时序。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ 已撤回的原始误判
|
||||||
|
|
||||||
|
以下条目经逐条代码验证后确认不成立或定级过高:
|
||||||
|
|
||||||
|
| 原始编号 | 原始结论 | 修正结论 |
|
||||||
|
|---------|---------|---------|
|
||||||
|
| ~~P0 引用循环~~ | Runtime/Coordinator 强引用 context 形成循环 | **不成立**。全量搜索确认所有 Coordinator/Runtime 均使用 `unowned let context` 或 `weak var context` |
|
||||||
|
| ~~P0 XSS 注入~~ | `javaScriptStringLiteral` 的 `]` 替换导致注入 | **不成立**。`JSONSerialization` 编码已处理引号/反斜杠等危险字符。实际是健壮性问题,非安全问题(已调整为 P2-2) |
|
||||||
|
| ~~P1 Publication 暴露 parser~~ | 外部可直接修改 parser 内部状态 | **不成立**。`RDEPUBParser` 关键属性均为 `public internal(set)`,外部模块无法修改 |
|
||||||
|
| ~~P2 NSRange 越界~~ | UTF-16 和 Swift String 混用导致偏移 | **不成立**。搜索代码全程在 `NSString` + `NSRange` 域内操作,未混合 Swift String 索引 |
|
||||||
|
| ~~P2 后台 deinit 崩溃~~ | teardownWebView 可能在后台线程执行 | **证据不足**。`RDEPUBWebView` 是 UIView 子类,正常在主线程释放,无证据表明会后台释放 |
|
||||||
|
| ~~P0 ZIP 路径遍历绕过~~ | `%2e%2e` 可绕过路径校验 | **论证过头**。当前实现有组件级 `..` 检查 + `standardizedFileURL` 前缀校验,双层防护有效 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📊 问题汇总
|
||||||
|
|
||||||
|
| 严重度 | 编号 | 问题 | 类型 |
|
||||||
|
|--------|------|------|------|
|
||||||
|
| 🟠 P1 | P1-1 | WKURLSchemeHandler 同步文件 I/O 阻塞调用线程 | 性能 |
|
||||||
|
| 🟠 P1 | P1-2 | EPUB 解压缓存目录无清理策略 | 存储 |
|
||||||
|
| 🟠 P1 | P1-3 | 搜索在主线程同步执行,大书可致 ANR | 性能 |
|
||||||
|
| 🟡 P2 | P2-1 | ZIP 无效条目导致整本打开失败 | 鲁棒性 |
|
||||||
|
| 🟡 P2 | P2-2 | javaScriptStringLiteral 实现依赖隐式假设 | 可维护性 |
|
||||||
|
| 🔵 P3 | P3-1 | 后台线程同步 hop 主线程获取布局信息 | 线程模型 |
|
||||||
|
|
||||||
|
**整体评价**: SDK 架构设计良好,模块分层清晰,引用管理(unowned/weak)使用正确,分页取消机制有 `paginationToken` + `cancellationController` 兜底。主要值得修复的是三个性能类 P1 问题(主线程阻塞搜索、资源加载阻塞、缓存无清理),两个 P2 鲁棒性/可维护性问题,以及一个 P3 线程模型优化建议。
|
||||||
@ -251,8 +251,7 @@ RDEPUBReaderController
|
|||||||
├─ RDEPUBPresentationRuntime // 分页状态与窗口替换
|
├─ RDEPUBPresentationRuntime // 分页状态与窗口替换
|
||||||
│ ├─ applyBookPageMap()
|
│ ├─ applyBookPageMap()
|
||||||
│ ├─ refreshBookPageMapInPlace()
|
│ ├─ refreshBookPageMapInPlace()
|
||||||
│ ├─ applyPendingFullPageMapIfNeeded()
|
│ └─ applyPendingFullPageMapIfNeeded()
|
||||||
│ └─ RDEPUBNavigationStateMachine
|
|
||||||
│
|
│
|
||||||
├─ RDEPUBChapterWarmupOrchestrator // 按需章节预热与边界预取
|
├─ RDEPUBChapterWarmupOrchestrator // 按需章节预热与边界预取
|
||||||
│ ├─ prepareOnDemandChapter()
|
│ ├─ prepareOnDemandChapter()
|
||||||
@ -495,7 +494,6 @@ Sources/RDReaderView/
|
|||||||
| **Builder** | `RDEPUBBookPageMap.Builder` 增量构建页码映射 |
|
| **Builder** | `RDEPUBBookPageMap.Builder` 增量构建页码映射 |
|
||||||
| **Strategy** | `RDEPUBTextRenderer` 协议,可替换渲染器实现 |
|
| **Strategy** | `RDEPUBTextRenderer` 协议,可替换渲染器实现 |
|
||||||
| **Pipeline** | `RDEPUBTextTypesetterPipeline` 排版管线(8 个逻辑阶段,封装为 5-6 个顶层调用) |
|
| **Pipeline** | `RDEPUBTextTypesetterPipeline` 排版管线(8 个逻辑阶段,封装为 5-6 个顶层调用) |
|
||||||
| **State Machine** | `RDEPUBNavigatorState` 管理阅读器状态转换 |
|
|
||||||
| **Adapter** | `RDReaderLegacyDataSourceAdapter` 适配旧数据源协议 |
|
| **Adapter** | `RDReaderLegacyDataSourceAdapter` 适配旧数据源协议 |
|
||||||
| **三级缓存** | 内存 → 磁盘摘要 → 全书分页,逐级降级 |
|
| **三级缓存** | 内存 → 磁盘摘要 → 全书分页,逐级降级 |
|
||||||
| **Token 取消** | `paginationToken` 确保过期异步任务不干扰新任务 |
|
| **Token 取消** | `paginationToken` 确保过期异步任务不干扰新任务 |
|
||||||
|
|||||||
411
Doc/AUDIT_FINAL.md
Normal file
411
Doc/AUDIT_FINAL.md
Normal file
@ -0,0 +1,411 @@
|
|||||||
|
# ReadViewSDK 代码审查报告
|
||||||
|
|
||||||
|
> 审查范围:`Sources/RDReaderView/` 全部 184 个 Swift 文件(约 32,300 行)
|
||||||
|
> 审查维度:并发安全、内存管理、API 正确性、Swift/iOS 平台特定、架构与依赖
|
||||||
|
> 审查日期:2026-06-26
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 严重程度定义
|
||||||
|
|
||||||
|
| 标记 | 含义 |
|
||||||
|
|------|------|
|
||||||
|
| 🔴 高 | 有明确代码路径可导致崩溃、数据丢失或严重功能缺陷 |
|
||||||
|
| 🟡 中 | 方向正确但风险程度取决于运行时条件;或当前安全但架构上脆弱 |
|
||||||
|
| 🟢 低 | 代码异味、可维护性问题或未来风险 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔴 高优先级问题
|
||||||
|
|
||||||
|
### H-01 · 内存警告未连接章节缓存清理
|
||||||
|
|
||||||
|
`RDEPUBChapterRuntimeStore.handleMemoryWarning()` 已实现(清空除当前章节外的缓存 + 清除图片缓存),但从未被调用。
|
||||||
|
|
||||||
|
- `RDEPUBReaderRuntime.handleMemoryWarning()` 只清理了 `backgroundCoverageStore`,未调用 `chapterRuntimeStore.handleMemoryWarning()`
|
||||||
|
- `RDEPUBReaderController` 未重写 `didReceiveMemoryWarning`
|
||||||
|
|
||||||
|
**影响:** 系统内存压力下,`RDEPUBChapterDataCache`(无上限字典)和 `imageCache`(仅 countLimit=50,无 totalCostLimit)不会主动释放内存。
|
||||||
|
|
||||||
|
**位置:**
|
||||||
|
- `RDEPUBChapterRuntimeStore.swift:103` — `handleMemoryWarning()` 方法存在
|
||||||
|
- `RDEPUBReaderRuntime.swift:606-620` — 只清理 backgroundCoverageStore
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### H-02 · 主线程信号量等待
|
||||||
|
|
||||||
|
`RDEPUBChapterLoader.loadChapterSynchronouslyForMigration()` 使用 `DispatchSemaphore.wait()` 阻塞调用线程。当从 `RDEPUBPresentationRuntime.rebindVisibleLocation()` 以 `allowSynchronousLoad: true` 调用时,会在主线程等待。
|
||||||
|
|
||||||
|
**影响:** 如果章节数据未缓存,主线程被阻塞直到章节构建完成,可能导致 ANR 或死锁(如果构建过程需要主线程资源)。
|
||||||
|
|
||||||
|
**位置:**
|
||||||
|
- `RDEPUBChapterLoader.swift:206-268` — `semaphore.wait()`
|
||||||
|
- `RDEPUBPresentationRuntime.swift:282-286` — `allowSynchronousLoad: true`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### H-03 · asyncAfter 闭包中的强制解包
|
||||||
|
|
||||||
|
`RDEPUBReaderController+DataSource.swift:425-436` 在 0.3 秒延迟闭包中强制解包 `lastTextPaginationPageSize!`。方法入口的 `guard let lastTextPaginationPageSize` 不保护延迟闭包——0.3 秒后该值可能已变为 `nil`。
|
||||||
|
|
||||||
|
**位置:**
|
||||||
|
```swift
|
||||||
|
// line 432-433
|
||||||
|
let stillChanged = abs(currentSize.width - self.lastTextPaginationPageSize!.width) > 0.5
|
||||||
|
|| abs(currentSize.height - self.lastTextPaginationPageSize!.height) > 0.5
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### H-04 · `unowned context` 在后台操作中的崩溃路径
|
||||||
|
|
||||||
|
以下两个类在后台操作中访问 `unowned let context: RDEPUBReaderContext`,若 controller 在操作期间释放则会崩溃:
|
||||||
|
|
||||||
|
**RDEPUBMetadataParseWorker:**
|
||||||
|
- Line 117: `let context = self.context` 在 `[weak self]` guard 之前将 unowned 引用捕获为局部变量
|
||||||
|
- Lines 197-324: `BlockOperation` 闭包直接使用该局部变量(unowned),无 `[weak self]` 保护
|
||||||
|
- Lines 296-301, 372-376: `DispatchQueue.main.async` 闭包捕获 unowned 局部变量
|
||||||
|
|
||||||
|
**RDEPUBReaderLoadCoordinator:**
|
||||||
|
- Line 28: 闭包只捕获 `[weak controller]`,未捕获 `[weak self]`
|
||||||
|
- Line 30: `self.context.makeParser()` 在后台队列访问 unowned 引用
|
||||||
|
- Lines 41-42, 53-54: `DispatchQueue.main.async` 闭包直接访问 `self.context`
|
||||||
|
|
||||||
|
**影响:** 若 `RDEPUBReaderController` 在后台操作期间释放(如用户快速退出阅读器),`unowned` 引用悬挂导致 EXC_BAD_ACCESS。
|
||||||
|
|
||||||
|
**位置:**
|
||||||
|
- `RDEPUBMetadataParseWorker.swift:117, 197-324, 296-301, 372-376`
|
||||||
|
- `RDEPUBReaderLoadCoordinator.swift:28-57`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### H-05 · 章节数据缓存无容量限制
|
||||||
|
|
||||||
|
`RDEPUBChapterDataCache` 是无上限的 `[Int: RDEPUBRuntimeChapter]` 字典,仅通过显式 `remove(spineIndex:)` 或 `removeAll()` 逐出。每章持有完整的 `typesetAttributedString`(50-200KB+),50 章可达 5-10MB+。
|
||||||
|
|
||||||
|
**位置:** `RDEPUBChapterDataCache.swift:5` — `private var storage: [Int: RDEPUBRuntimeChapter] = [:]`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### H-06 · 暗色图片静态 NSCache 无 totalCostLimit
|
||||||
|
|
||||||
|
`RDEPUBDarkImageAdjuster.imageCache` 是 static `NSCache<NSString, UIImage>()`,无 `totalCostLimit`,跨越整个 App 生命周期累积调整后的图片。
|
||||||
|
|
||||||
|
**位置:** `RDEPUBDarkImageAdjuster.swift:9`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### H-07 · 持久化操作静默丢弃错误
|
||||||
|
|
||||||
|
`RDEPUBReaderPersistence` 中所有编解码操作均使用 `try?`,失败时:
|
||||||
|
- **保存**:静默返回,数据丢失无任何反馈(lines 84, 98, 112, 131)
|
||||||
|
- **加载**:返回 `nil` 或空数组,损坏/版本不匹配的数据无法恢复(lines 80, 94, 108, 127)
|
||||||
|
|
||||||
|
**影响:** 用户书签和阅读位置可能在无声中丢失。
|
||||||
|
|
||||||
|
**位置:** `RDEPUBReaderPersistence.swift:76-131`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### H-08 · WKURLSchemeHandler 线程违规与取消回调缺失
|
||||||
|
|
||||||
|
两个确定 bug:
|
||||||
|
|
||||||
|
**(a) 不同队列回调:** `webView(_:start:)` 在某个串行队列(由 WebKit 调度)上被调用,但 `respondWithInMemoryData` 和 `respondWithStreaming` 将 `didReceive`/`didFinish`/`didFailWithError` 显式切到了 `ioQueue`(line 121, 151)。这至少违反了"回调应与 `start` 使用同一串行队列"的要求。
|
||||||
|
|
||||||
|
**(b) 取消时不调用完成回调:** 任务被 `stop` 后,`isTaskActive` 返回 false 时:
|
||||||
|
- streaming 路径(line 178):`guard self.isTaskActive(taskID) else { return }` — 仅 return,不调用 `didFinish` 或 `didFailWithError`
|
||||||
|
- in-memory 路径(line 128, 141):同理
|
||||||
|
|
||||||
|
这违反了"每个 started task 必须收到完成回调"的协议要求。
|
||||||
|
|
||||||
|
**位置:** `RDEPUBResourceURLSchemeHandler.swift:121-187`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### H-09 · 搜索在后台线程同步加载所有章节
|
||||||
|
|
||||||
|
`RDEPUBReaderSearchCoordinator` 在 `searchQueue` 上遍历所有 buildable spine 索引,逐个同步加载章节。每章持有完整 `typesetAttributedString`,大型书籍搜索时内存峰值显著。
|
||||||
|
|
||||||
|
**位置:** `RDEPUBReaderSearchCoordinator.swift:47, 173-236`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### H-10 · 依赖版本未锁定
|
||||||
|
|
||||||
|
`RDReaderView.podspec` 中 DTCoreText、SnapKit、SSAlertSwift 无版本约束:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
s.dependency 'ZIPFoundation', '~> 0.9' # ✅ 已锁定
|
||||||
|
s.dependency 'DTCoreText' # ❌ 无约束
|
||||||
|
s.dependency 'SnapKit' # ❌ 无约束
|
||||||
|
s.dependency 'SSAlertSwift' # ❌ 无约束
|
||||||
|
```
|
||||||
|
|
||||||
|
**位置:** `RDReaderView.podspec:17-20`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### H-11 · SnapKit 和 SSAlertSwift 疑似死依赖
|
||||||
|
|
||||||
|
全仓库搜索未找到任何 `snp.` 或 `SSAlert` 的使用点。这两个依赖可能增加了不必要的二进制体积和潜在冲突。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🟡 中优先级问题
|
||||||
|
|
||||||
|
### M-01 · `RDEPUBReaderState` 无同步保护(隐性风险)
|
||||||
|
|
||||||
|
所有属性(`parser`, `publication`, `activeBookmarks`, `pendingPageMapUpdates` 等)均为 `var`,无锁、无 `@MainActor`、无 `dispatchPrecondition`。当前所有访问确实在主线程(经追踪 16 个 `pendingPageMapUpdates` 访问点确认),但缺少形式化保障——未来开发者可能无意从后台线程访问而不自知。
|
||||||
|
|
||||||
|
**位置:** `RDEPUBReaderState.swift:1-71`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### M-02 · `currentSpineIndex` / `windowSpineIndices` 无锁但当前安全
|
||||||
|
|
||||||
|
这两个字段无锁保护,但追踪全部读写路径后确认当前均在主线程。风险是隐性的:store 对象被传入 `chapterLoadQueue` 上下文,未来开发者可能在该队列中访问这些属性而不自知。同文件的其他字段(`navigationLock`, `prefetchLock`, `buildingLock`, `cfiMapLock`)有锁保护,形成不一致的模式。
|
||||||
|
|
||||||
|
**位置:** `RDEPUBChapterRuntimeStore.swift:15-17`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### M-03 · `warmAnchors` 跨线程访问无同步
|
||||||
|
|
||||||
|
`RDEPUBBackgroundPriorityManager.warmAnchors` 在主线程写入(`addWarmAnchor`),在 `MetadataParseWorker` 的后台线程读取(`makeMetadataPriorityOrder`),无锁保护。
|
||||||
|
|
||||||
|
**位置:** `RDEPUBBackgroundPriorityPolicy.swift:65`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### M-04 · `assert(Thread.isMainThread)` 在 Release 中被移除
|
||||||
|
|
||||||
|
`RDEPUBReaderContext` 的 `makeLayoutSnapshot()`、`currentTextPageSize()` 等方法使用 `assert(Thread.isMainThread)` 检查。`assert` 在 `-O` 构建中被移除,生产环境中后台线程调用将静默访问 UIKit 属性,造成数据竞争且无诊断。
|
||||||
|
|
||||||
|
**应使用:** `dispatchPrecondition(condition: .onQueue(.main))` 或 `@MainActor` 标注。
|
||||||
|
|
||||||
|
**位置:** `RDEPUBReaderContext.swift:172, 188, 210, 215`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### M-05 · 磁盘缓存无自动淘汰策略
|
||||||
|
|
||||||
|
`RDEPUBChapterSummaryDiskCache` 和 `RDEPUBTextBookCache` 只有全量清除操作(`removeAll()`、`invalidateAll()`),无 LRU/大小/年龄驱逐策略。每次设置变更(字体、行距、主题)产生新缓存键,旧文件无限累积。写操作使用原子替换(tmp + replace),交错不会损坏数据,但可能读到旧值或看到不一致的目录状态。
|
||||||
|
|
||||||
|
**位置:**
|
||||||
|
- `RDEPUBChapterSummaryDiskCache.swift:89` — `removeAll()` 全清
|
||||||
|
- `RDEPUBTextBookCache.swift:191` — `invalidateAll()` 全清
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### M-06 · CFI 范围分隔符可能与内容冲突
|
||||||
|
|
||||||
|
`RDEPUBCFICompatibility` 按 `".."` 和 `"-"` 分割 CFI 范围字符串,但这些字符可能出现在 CFI 文本断言内部(如 `[foo..bar]`)。代码先匹配 `".."` 再匹配 `"-"`,无歧义消解逻辑。
|
||||||
|
|
||||||
|
**位置:** `RDEPUBCFICompatibility.swift:18-21`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### M-07 · CFI 步索引对奇数输入容错不严谨
|
||||||
|
|
||||||
|
`RDEPUBCFIResolver` 中 `steps[1].index / 2 - 1` 对奇数索引做整除截断,可能映射到错误章节。`max(..., 0)` 仅防负索引,不防错误映射。属于 EPUB CFI 规范的输入验证问题。
|
||||||
|
|
||||||
|
**位置:** `RDEPUBCFIResolver.swift:38`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### M-08 · WKWebView 保留环需主动管理
|
||||||
|
|
||||||
|
`RDEPUBWebView` → `webView` → `configuration.userContentController` → `RDEPUBWebView`(作为 `WKScriptMessageHandler`)形成保留环。
|
||||||
|
|
||||||
|
主要解环路径是 `reset()` 和 `configureWebViewIfNeeded` 中主动调用 `teardownWebView()`。**注意:** `deinit` 虽也调用 `teardownWebView()`,但不能作为环会被打破的保证——保留环的存在意味着 `deinit` 可能永远不会触发。风险在于:若某条代码路径跳过了 `teardownWebView()`,环将持续且对象永远不会释放。
|
||||||
|
|
||||||
|
**位置:**
|
||||||
|
- `RDEPUBWebView.swift:129` — `deinit` 调用 `teardownWebView()`(不可靠兜底)
|
||||||
|
- `RDEPUBWebView.swift:138-160` — `reset()` 调用 `teardownWebView()`(主要解环路径)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### M-09 · `configuration` didSet 在 `isViewLoaded` 之前持久化
|
||||||
|
|
||||||
|
`RDEPUBReaderController.configuration` 的 `didSet` 在 `guard isViewLoaded` 之前执行了 `readerContext.configuration = configuration`、`applyWebViewDebugPolicy()` 和 `persistReaderSettingsIfNeeded()`。若在 view 加载前设置 configuration,这些副作用会在 UI 未就绪时触发。
|
||||||
|
|
||||||
|
**位置:** `RDEPUBReaderController.swift:8-20`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### M-10 · empty catch 吞掉章节加载错误
|
||||||
|
|
||||||
|
`RDEPUBReaderPaginationCoordinator` 中 `loadInitialInteractiveRuntimeChapters` 的 `catch {}` 为空,章节加载失败被静默忽略,无日志、无恢复。
|
||||||
|
|
||||||
|
**位置:** `RDEPUBReaderPaginationCoordinator.swift:283`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### M-11 · `RDEPUBReaderFontChoice.displayName` 始终返回中文
|
||||||
|
|
||||||
|
枚举的 `displayName` 计算属性硬编码中文字符串("系统"、"宋体"、"圆体"、"等宽"),无本地化基础设施。
|
||||||
|
|
||||||
|
**位置:** `RDEPUBReaderConfiguration.swift:18-28`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### M-12 · 大量硬编码中文字符串,本地化覆盖不完整
|
||||||
|
|
||||||
|
覆盖文件:`RDEPUBReaderSettingsViewController`、`RDEPUBReaderSearchBarView`、`RDEPUBReaderHighlightsViewController`、`RDEPUBReaderBookmarksViewController`、`RDEPUBReaderTopToolView`、`RDEPUBReaderBottomToolView`、`RDEPUBTextContentView`、`RDEPUBWebView+Configuration`、`RDEPUBChapterLoader`(错误信息)等。
|
||||||
|
|
||||||
|
项目中存在约 88 个硬编码中文字符串(如"拷贝"、"高亮"、"批注"、"目录"、"搜索"等),但同时也有使用 `NSLocalizedString` 的地方(如 `RDEPUBImageViewController.swift:126`)。本地化基础设施存在但覆盖不完整——新添加的 UI 文本大多直接硬编码中文而未走本地化流程。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### M-13 · DTCoreText 类型泄漏到 EPUBUI 层
|
||||||
|
|
||||||
|
5 个 EPUBUI 文件直接使用 `DTCoreTextLayoutFrame`、`DTCoreTextLayoutLine`、`DTCoreTextGlyphRun` 等 DTCoreText 类型(虽有 `#if canImport(DTCoreText)` 保护),意味着替换渲染引擎需修改 UI 层文件。
|
||||||
|
|
||||||
|
**位置:**
|
||||||
|
- `RDEPUBPageInteractionController.swift`
|
||||||
|
- `RDEPUBPageLayoutSnapshot.swift`
|
||||||
|
- `RDEPUBTextPageRenderView.swift`
|
||||||
|
- `RDEPUBTextContentView.swift`
|
||||||
|
- `RDEPUBDarkImageAdjuster.swift`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### M-14 · `imageCache` 有 countLimit 但无 totalCostLimit
|
||||||
|
|
||||||
|
`RDEPUBChapterRuntimeStore.imageCache` 设有 `countLimit = 50`,但无 `totalCostLimit`。50 张大尺寸书籍插图可达 150-250MB。
|
||||||
|
|
||||||
|
**位置:** `RDEPUBChapterRuntimeStore.swift:41`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### M-15 · `UIMenuController` 已废弃
|
||||||
|
|
||||||
|
`RDEPUBWebView+Configuration.swift:39` 和 `RDEPUBTextContentView.swift:747-758` 使用 `UIMenuController`,该 API 从 iOS 16 起废弃。应迁移至 `UIEditMenuInteraction`(iOS 16+),并为 iOS 15 保留 fallback。当前代码在 iOS 17+ 上仍可工作但随时可能失效。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### M-16 · Dynamic Type 未支持
|
||||||
|
|
||||||
|
UI 中所有字体使用硬编码大小(`UIFont.systemFont(ofSize: 13/14/16/18/19)`),未使用 `UIFontMetrics` 或 `preferredFont(forTextStyle:)`。启用 Dynamic Type 的用户看不到文字大小变化。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### M-17 · URL Scheme Handler 取消时 `clearTask` 可能被跳过
|
||||||
|
|
||||||
|
`respondWithInMemoryData` line 128 的 `guard self.isTaskActive(taskID) else { return }` 在取消时直接返回,跳过了 line 146 的 `self.clearTask(taskID)`。当前因 `stop` 已调用 `clearTask` 而无害,但结构上脆弱。
|
||||||
|
|
||||||
|
**位置:** `RDEPUBResourceURLSchemeHandler.swift:128`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### M-18 · 硬编码颜色值未集成到主题系统
|
||||||
|
|
||||||
|
`RDEPUBReaderSearchBarView` 含 18 个硬编码颜色字面量,默认高亮颜色 `"#F8E16C"` 硬编码在 `RDEPUBReaderController+PublicAPI.swift:65,75`。这些应属于 `RDEPUBReaderTheme`。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### M-19 · 硬编码时间常量
|
||||||
|
|
||||||
|
多个 `DispatchQueue.main.asyncAfter(deadline:)` 和 `Thread.sleep` 使用硬编码延迟值(0.08s、0.1s、0.15s、0.2s、0.25s、0.3s、1.0s、3.5s),无命名常量或可配置选项。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🟢 低优先级问题
|
||||||
|
|
||||||
|
### L-01 · `RDEPUBChapterLoader` 中 `[self]` 强捕获
|
||||||
|
|
||||||
|
Line 103 的 `store.chapterLoadQueue.async { [self] in` 强捕获 self,阻止 loader 在队列任务完成前释放。应改为 `[weak self]`。
|
||||||
|
|
||||||
|
**位置:** `RDEPUBChapterLoader.swift:103`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### L-02 · MetadataParseWorker Thread.sleep 忙等待
|
||||||
|
|
||||||
|
`waitForReadingInteractionToSettle()` 使用 `Thread.sleep(forTimeInterval: 0.08)` 轮询,无上限等待。应考虑 `DispatchSource` 或条件变量。
|
||||||
|
|
||||||
|
**位置:** `RDEPUBMetadataParseWorker.swift:385-390`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### L-03 · `RDEPUBTextBookCache.queue.sync` 潜在死锁
|
||||||
|
|
||||||
|
`load()` 和 `save()` 均使用 `queue.sync`。若从 queue 内部调用则会死锁。当前未发现此调用路径,但模式脆弱。
|
||||||
|
|
||||||
|
**位置:** `RDEPUBTextBookCache.swift:136-189`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### L-04 · `pendingLoads` 字典无超时清理
|
||||||
|
|
||||||
|
`RDEPUBChapterLoader.pendingLoads` 的完成闭包数组在章节加载失败且 `resolvePendingLoad` 未被调用时可能累积。当前所有路径均调用 `resolvePendingLoad`,但无防御性超时。
|
||||||
|
|
||||||
|
**位置:** `RDEPUBChapterLoader.swift:7-10`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### L-05 · `RDEPUBChapterWindowCoordinator` 是死代码
|
||||||
|
|
||||||
|
该类定义了但未被任何代码实例化或引用。其中对 `currentSpineIndex`/`windowSpineIndices` 的7个访问点均为死代码。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### L-06 · 平台版本不一致
|
||||||
|
|
||||||
|
- `podspec`: `s.platform = :ios, "15.0"`
|
||||||
|
- `Podfile`(根和 demo): `platform :ios, '15.6'`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### L-07 · `RDEPUBMetadataCancellationController` 微小竞争
|
||||||
|
|
||||||
|
`attach()` 方法在 `lock.unlock()` 和 `queue.cancelAllOperations()` 之间存在窄窗口,新操作可能被添加到队列但不会被取消。实际影响极小。
|
||||||
|
|
||||||
|
**位置:** `RDEPUBMetadataParseCancellationController.swift:attach()`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### L-08 · 页码标签硬编码偏移
|
||||||
|
|
||||||
|
`RDEPUBWebContentView` 和 `RDEPUBTextContentView` 的页码标签使用硬编码偏移(24pt, 20pt),在刘海屏设备上可能与安全区域重叠。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 已排除项
|
||||||
|
|
||||||
|
以下原报告条目经核实后排除或合并:
|
||||||
|
|
||||||
|
| 原编号 | 原描述 | 排除原因 |
|
||||||
|
|--------|--------|----------|
|
||||||
|
| #6 | compactMap 结果越界 | 入口有 `childViewControllers.count == expectedCount` 检查,compactMap 后不会少于预期数量 |
|
||||||
|
| #7 | `result!` 强解包崩溃 | 信号量等待的真实风险是无限等待/卡住,不是 result 为 nil;signal 前必定赋值 |
|
||||||
|
| #21 | 搜索协调器 fallback 空 store | `?? RDEPUBChapterRuntimeStore()` 前的可选链若为 nil 则整个调用返回 nil,fallback 不会生效 |
|
||||||
|
| #23 | init 时加载所有 HTML 到内存 | 逐章取 HTML 算哈希后只保存 digest,HTML 字符串不长期保留 |
|
||||||
|
| #35 | `.nonPersistent()` 阻止 cookie/localStorage | 不持久化是有意设计,不是 bug;原始报告将"不持久化"误述为"不能用" |
|
||||||
|
| #48 | `didMoveToSuperview` 每次重复 makeUI | 有 `didBuildUI` 防重入保护 |
|
||||||
|
| 原L-04 | `activeTasks` 字典 `clearTask` 设 nil 不删键 | Swift 中 `dict[key] = nil` 等同于 `removeValue(forKey:)`,不保留空洞 |
|
||||||
|
| 原M-18 | MetadataParseWorker OperationQueue 未在 deinit 取消 | 外层 `[weak self]` guard 确保 self 存活才执行 queue 操作;`cancellationController` 提供显式取消路径 |
|
||||||
|
| 原L-08 | `ss_superViewController` 前缀暗示外部来源 | 命名风格猜测,非可验证缺陷 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 建议优先级排序
|
||||||
|
|
||||||
|
| 优先级 | 编号 | 修复复杂度 | 说明 |
|
||||||
|
|--------|------|-----------|------|
|
||||||
|
| P0 | H-01 | 低 | 在 `handleMemoryWarning` 中加一行 `chapterRuntimeStore.handleMemoryWarning()`,并在 controller 中转发 `didReceiveMemoryWarning` |
|
||||||
|
| P0 | H-04 | 中 | MetadataParseWorker 和 LoadCoordinator 中 `unowned context` 改为 `weak`,闭包加 `guard` |
|
||||||
|
| P0 | H-03 | 低 | `lastTextPaginationPageSize!` 改为 `guard let` 安全解包 |
|
||||||
|
| P1 | H-08 | 中 | URLSchemeHandler 回调改到与 start 相同队列,取消路径加 `didFailWithError` |
|
||||||
|
| P1 | H-02 | 中 | 主线程同步加载改为异步回调或确保不从主线程调用 |
|
||||||
|
| P1 | H-07 | 低 | 关键持久化操作加日志,至少记录失败原因 |
|
||||||
|
| P1 | H-10 | 低 | 给三个依赖加版本约束;移除 SnapKit 和 SSAlertSwift |
|
||||||
|
| P2 | H-05 | 中 | 为 ChapterDataCache 加 LRU 逐出策略或最大条目数限制 |
|
||||||
|
| P2 | H-06 | 低 | 为 DarkImageAdjuster.imageCache 设置 `totalCostLimit` |
|
||||||
|
| P2 | H-09 | 中 | 搜索改为流式/分批加载,避免一次性持有所有章节 |
|
||||||
|
| P2 | M-01/02 | 中 | 为 State 和 Store 关键属性加 `@MainActor` 或 `dispatchPrecondition` |
|
||||||
|
| P2 | M-04 | 低 | `assert` 改为 `dispatchPrecondition` 或 `precondition` |
|
||||||
|
| P2 | M-08 | 低 | 保留环加注释说明必须通过 `reset()` 打破 |
|
||||||
|
| P2 | M-15 | 中 | 迁移至 `UIEditMenuInteraction` |
|
||||||
@ -4,7 +4,7 @@ Pod::Spec.new do |s|
|
|||||||
s.name = "RDReaderView"
|
s.name = "RDReaderView"
|
||||||
s.version = "0.0.1"
|
s.version = "0.0.1"
|
||||||
s.summary = "A reader view for novel"
|
s.summary = "A reader view for novel"
|
||||||
s.platform = :ios, "15.0"
|
s.platform = :ios, "15.6"
|
||||||
s.swift_versions = ["5.10"]
|
s.swift_versions = ["5.10"]
|
||||||
s.homepage = "https://github.com/namesubai/RDReaderView.git"
|
s.homepage = "https://github.com/namesubai/RDReaderView.git"
|
||||||
s.author = { "subai" => "804663401@qq.com" }
|
s.author = { "subai" => "804663401@qq.com" }
|
||||||
@ -15,9 +15,7 @@ Pod::Spec.new do |s|
|
|||||||
'RDReaderViewAssets' => ['Sources/RDReaderView/EPUBCore/Resources/**/*']
|
'RDReaderViewAssets' => ['Sources/RDReaderView/EPUBCore/Resources/**/*']
|
||||||
}
|
}
|
||||||
s.dependency 'ZIPFoundation', '~> 0.9'
|
s.dependency 'ZIPFoundation', '~> 0.9'
|
||||||
s.dependency 'DTCoreText'
|
s.dependency 'DTCoreText', '~> 1.6'
|
||||||
s.dependency 'SnapKit'
|
|
||||||
s.dependency 'SSAlertSwift'
|
|
||||||
s.requires_arc = true
|
s.requires_arc = true
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@ -17,12 +17,8 @@ PODS:
|
|||||||
- DTFoundation/UIKit (1.7.19):
|
- DTFoundation/UIKit (1.7.19):
|
||||||
- DTFoundation/Core
|
- DTFoundation/Core
|
||||||
- RDReaderView (0.0.1):
|
- RDReaderView (0.0.1):
|
||||||
- DTCoreText
|
- DTCoreText (~> 1.6)
|
||||||
- SnapKit
|
|
||||||
- SSAlertSwift
|
|
||||||
- ZIPFoundation (~> 0.9)
|
- ZIPFoundation (~> 0.9)
|
||||||
- SnapKit (5.7.1)
|
|
||||||
- SSAlertSwift (0.0.15)
|
|
||||||
- ZIPFoundation (0.9.20)
|
- ZIPFoundation (0.9.20)
|
||||||
|
|
||||||
DEPENDENCIES:
|
DEPENDENCIES:
|
||||||
@ -32,8 +28,6 @@ SPEC REPOS:
|
|||||||
trunk:
|
trunk:
|
||||||
- DTCoreText
|
- DTCoreText
|
||||||
- DTFoundation
|
- DTFoundation
|
||||||
- SnapKit
|
|
||||||
- SSAlertSwift
|
|
||||||
- ZIPFoundation
|
- ZIPFoundation
|
||||||
|
|
||||||
EXTERNAL SOURCES:
|
EXTERNAL SOURCES:
|
||||||
@ -43,9 +37,7 @@ EXTERNAL SOURCES:
|
|||||||
SPEC CHECKSUMS:
|
SPEC CHECKSUMS:
|
||||||
DTCoreText: 11b7fe2104f476f82e75a4e3dbdde74d7186cecb
|
DTCoreText: 11b7fe2104f476f82e75a4e3dbdde74d7186cecb
|
||||||
DTFoundation: 76b624967cf5bcaae6bb057d622c536c36ef36d0
|
DTFoundation: 76b624967cf5bcaae6bb057d622c536c36ef36d0
|
||||||
RDReaderView: 617ec758a5db3c10024acf83b110465ea8343b5b
|
RDReaderView: 2e0eeeff4bcfe8bbc09642344d326440af280ed5
|
||||||
SnapKit: d612e99e678a2d3b95bf60b0705ed0a35c03484a
|
|
||||||
SSAlertSwift: aad8dc0c20b36fcffe700b81d7be89d60c7ba4f1
|
|
||||||
ZIPFoundation: dfd3d681c4053ff7e2f7350bc4e53b5dba3f5351
|
ZIPFoundation: dfd3d681c4053ff7e2f7350bc4e53b5dba3f5351
|
||||||
|
|
||||||
PODFILE CHECKSUM: 775f5c8c488024e24d494aad6331e9fef8f9e2e5
|
PODFILE CHECKSUM: 775f5c8c488024e24d494aad6331e9fef8f9e2e5
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"summary": "A reader view for novel",
|
"summary": "A reader view for novel",
|
||||||
"platforms": {
|
"platforms": {
|
||||||
"ios": "15.0"
|
"ios": "15.6"
|
||||||
},
|
},
|
||||||
"swift_versions": [
|
"swift_versions": [
|
||||||
"5.10"
|
"5.10"
|
||||||
@ -28,13 +28,7 @@
|
|||||||
"~> 0.9"
|
"~> 0.9"
|
||||||
],
|
],
|
||||||
"DTCoreText": [
|
"DTCoreText": [
|
||||||
|
"~> 1.6"
|
||||||
],
|
|
||||||
"SnapKit": [
|
|
||||||
|
|
||||||
],
|
|
||||||
"SSAlertSwift": [
|
|
||||||
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"requires_arc": true,
|
"requires_arc": true,
|
||||||
|
|||||||
12
ReadViewDemo/Pods/Manifest.lock
generated
12
ReadViewDemo/Pods/Manifest.lock
generated
@ -17,12 +17,8 @@ PODS:
|
|||||||
- DTFoundation/UIKit (1.7.19):
|
- DTFoundation/UIKit (1.7.19):
|
||||||
- DTFoundation/Core
|
- DTFoundation/Core
|
||||||
- RDReaderView (0.0.1):
|
- RDReaderView (0.0.1):
|
||||||
- DTCoreText
|
- DTCoreText (~> 1.6)
|
||||||
- SnapKit
|
|
||||||
- SSAlertSwift
|
|
||||||
- ZIPFoundation (~> 0.9)
|
- ZIPFoundation (~> 0.9)
|
||||||
- SnapKit (5.7.1)
|
|
||||||
- SSAlertSwift (0.0.15)
|
|
||||||
- ZIPFoundation (0.9.20)
|
- ZIPFoundation (0.9.20)
|
||||||
|
|
||||||
DEPENDENCIES:
|
DEPENDENCIES:
|
||||||
@ -32,8 +28,6 @@ SPEC REPOS:
|
|||||||
trunk:
|
trunk:
|
||||||
- DTCoreText
|
- DTCoreText
|
||||||
- DTFoundation
|
- DTFoundation
|
||||||
- SnapKit
|
|
||||||
- SSAlertSwift
|
|
||||||
- ZIPFoundation
|
- ZIPFoundation
|
||||||
|
|
||||||
EXTERNAL SOURCES:
|
EXTERNAL SOURCES:
|
||||||
@ -43,9 +37,7 @@ EXTERNAL SOURCES:
|
|||||||
SPEC CHECKSUMS:
|
SPEC CHECKSUMS:
|
||||||
DTCoreText: 11b7fe2104f476f82e75a4e3dbdde74d7186cecb
|
DTCoreText: 11b7fe2104f476f82e75a4e3dbdde74d7186cecb
|
||||||
DTFoundation: 76b624967cf5bcaae6bb057d622c536c36ef36d0
|
DTFoundation: 76b624967cf5bcaae6bb057d622c536c36ef36d0
|
||||||
RDReaderView: 617ec758a5db3c10024acf83b110465ea8343b5b
|
RDReaderView: 2e0eeeff4bcfe8bbc09642344d326440af280ed5
|
||||||
SnapKit: d612e99e678a2d3b95bf60b0705ed0a35c03484a
|
|
||||||
SSAlertSwift: aad8dc0c20b36fcffe700b81d7be89d60c7ba4f1
|
|
||||||
ZIPFoundation: dfd3d681c4053ff7e2f7350bc4e53b5dba3f5351
|
ZIPFoundation: dfd3d681c4053ff7e2f7350bc4e53b5dba3f5351
|
||||||
|
|
||||||
PODFILE CHECKSUM: 775f5c8c488024e24d494aad6331e9fef8f9e2e5
|
PODFILE CHECKSUM: 775f5c8c488024e24d494aad6331e9fef8f9e2e5
|
||||||
|
|||||||
4898
ReadViewDemo/Pods/Pods.xcodeproj/project.pbxproj
generated
4898
ReadViewDemo/Pods/Pods.xcodeproj/project.pbxproj
generated
File diff suppressed because it is too large
Load Diff
110
ReadViewDemo/Pods/SSAlertSwift/README.md
generated
110
ReadViewDemo/Pods/SSAlertSwift/README.md
generated
@ -1,110 +0,0 @@
|
|||||||
# SSAlert
|
|
||||||
实际开发中经常遇到各种自定义的弹窗的需求,其中编写弹窗动画和遮罩就很繁琐,而且一个一个去写重复的代码,代码会变得更加冗余。
|
|
||||||
|
|
||||||
SSAlert封装了动画部分,而且支持自定义动画。可以快速构建弹窗,简单易用,基本可以满足大部分弹窗需求。自带常用自定义弹窗,类型系统的UIAlertView,UIActionSheet。
|
|
||||||
# 特性
|
|
||||||
- 支持OC、Swift版本
|
|
||||||
- 支持自定义弹窗内容,可以根据弹窗内容frame布局和自动布局确定弹窗大小
|
|
||||||
- 支持动画展示后刷新弹窗的大小
|
|
||||||
- 支带透明、黑色、无三种背景遮罩
|
|
||||||
- 自带从上、下、左、右、中弹出动画,动画支持弹簧效果,而且可以设置位置偏移量
|
|
||||||
- 支持自定义动画,动画隐藏回调
|
|
||||||
- 支持模态视图弹窗,支持拖拽隐藏
|
|
||||||
- 自带类似系统的弹窗UIAlertView,UIActionSheet
|
|
||||||
# 效果图
|
|
||||||
<img src="https://github.com/namesubai/SSAlert/blob/main/demo.gif" width = 20% height = 20% />
|
|
||||||
|
|
||||||
# 使用
|
|
||||||
|
|
||||||
## 1.导入代码
|
|
||||||
|
|
||||||
### Objective-C项目
|
|
||||||
|
|
||||||
1. pod 'SSAlert'
|
|
||||||
2. #import <SSAlert/SSAlert.h>
|
|
||||||
### Swift项目
|
|
||||||
|
|
||||||
1. pod 'SSAlertSwift'
|
|
||||||
2. import SSAlertSwift
|
|
||||||
|
|
||||||
## 2.SSAlertView用法
|
|
||||||
|
|
||||||
### SSAlertView的初始化
|
|
||||||
|
|
||||||
SSAlertView的初始化有两种,一种是普通弹窗,一种是模态视图弹窗,两种的区别在于,前者是弹出一个View,后者是弹窗一个UIViewController。当需要点击弹窗上按钮不关闭弹窗跳转界面时候,可以使用模态视图弹窗。
|
|
||||||
|
|
||||||
普通弹窗初始化:
|
|
||||||
```
|
|
||||||
let customView = UIView()
|
|
||||||
customView.frame = CGRect(x:0,y:0,width:200,height:200)
|
|
||||||
let alertView = SSAlertView(customView: customView, onView: navigationController!.view)
|
|
||||||
```
|
|
||||||
模态视图弹窗初始化:
|
|
||||||
```
|
|
||||||
let customView = UIView()
|
|
||||||
customView.frame = CGRect(x:0,y:0,width:200,height:200)
|
|
||||||
let alertView = SSAlertView(customView: customView, fromViewController: self)
|
|
||||||
```
|
|
||||||
### SSAlertView的动画设置
|
|
||||||
|
|
||||||
遵循 **SSAlertAnimation** 协议的自定义动画都可以设置 **SSAlertView** 的 **animation** 属性
|
|
||||||
|
|
||||||
**SSAlertView**自带的动画效果:**SSAlertDefaultAnmation**,提供上、下、左、右、中弹出动画等
|
|
||||||
```
|
|
||||||
let animation = SSAlertDefaultAnmation(state: .fromCenter)
|
|
||||||
alertView.animation = animation
|
|
||||||
```
|
|
||||||
### SSAlertView的展示和隐藏
|
|
||||||
```
|
|
||||||
alertView.show()
|
|
||||||
alertView.hide()
|
|
||||||
```
|
|
||||||
### SSAlertView的大小刷新
|
|
||||||
```
|
|
||||||
customView.frame = CGRect(x:0,y:0,width:400,height:400)
|
|
||||||
alertView.refreshFrame()
|
|
||||||
```
|
|
||||||
|
|
||||||
## 3.自带类型系统UIAlert,UIActionSheet的用法
|
|
||||||
```
|
|
||||||
///
|
|
||||||
let alertView = SSAlertView.alertView(style: .alert, title: "自定义Alert弹窗", message: "自带自定义Alert弹窗,类似系统的UIAlertView", cancelButton: "Cancel", otherButtons: ["OK"], onView: navigationController!.view){ index in
|
|
||||||
print(index)
|
|
||||||
}
|
|
||||||
alertView.show()
|
|
||||||
|
|
||||||
///
|
|
||||||
let alertView = SSAlertView.modalAlertView(style: .actionSheet, title: "自定义ActionSheet弹窗", message: "自带自定义ActionSheet弹窗,类似系统的ActionSheet", cancelButton: "Cancel", otherButtons: ["action1", "action2", "action3", "action4"], fromViewController: self) { index in
|
|
||||||
print(index)
|
|
||||||
}
|
|
||||||
alertView.show()
|
|
||||||
```
|
|
||||||
|
|
||||||
## 4.自定义SSAlertCommonView
|
|
||||||
```
|
|
||||||
let action1 = SSAlertAction(style: .actionSheet, title: "自定义Action1") {
|
|
||||||
print("自定义Action1")
|
|
||||||
}
|
|
||||||
action1.titleColor = .red
|
|
||||||
action1.backgroundColor = .yellow
|
|
||||||
action1.titleFont = UIFont.systemFont(ofSize: 18)
|
|
||||||
action1.height = 80
|
|
||||||
|
|
||||||
let action2 = SSAlertAction(style: .actionSheet, title: "自定义Action2") {
|
|
||||||
print("自定义Action2")
|
|
||||||
}
|
|
||||||
action2.titleColor = .yellow
|
|
||||||
action2.backgroundColor = .red
|
|
||||||
action2.titleFont = UIFont.systemFont(ofSize: 22)
|
|
||||||
action2.height = 55
|
|
||||||
|
|
||||||
let commonView = SSAlertCommonView(title: "自定义SSAlertCommonView", message: "文本文本文本文本文本文本", style: .actionSheet, actions: [action1, action2])
|
|
||||||
commonView.backgroundColor = .white
|
|
||||||
let alertView = SSAlertView(customView: commonView, onView: navigationController!.view, animation: SSAlertDefaultAnmation(state: .fromBottom))
|
|
||||||
alertView.show()
|
|
||||||
```
|
|
||||||
|
|
||||||
更多用法请下载代码,观看[demo](https://github.com/namesubai/SSAlert),
|
|
||||||
|
|
||||||
觉得好用,手动点个Star❤️❤️❤️❤️
|
|
||||||
|
|
||||||
@ -1,24 +0,0 @@
|
|||||||
//
|
|
||||||
// SSAlertAnimation.swift
|
|
||||||
// SSAlert
|
|
||||||
//
|
|
||||||
// Created by yangsq on 2021/8/13.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
public typealias PanProgress = CGFloat
|
|
||||||
public typealias PanCancelProgress = CGFloat
|
|
||||||
public typealias DimissIsCancel = Bool
|
|
||||||
public protocol SSAlertAnimation {
|
|
||||||
/// 动画时间
|
|
||||||
func animationDuration() -> TimeInterval
|
|
||||||
/// 展示动画
|
|
||||||
func showAnimationOfAnimationView(animationView: SSAlertView, viewSize: CGSize, animated: Bool, completion:(((Bool) -> Void))?)
|
|
||||||
/// 隐藏动画
|
|
||||||
func hideAnimationOfAnimationView(animationView: SSAlertView, viewSize: CGSize, animated: Bool, completion:(((Bool) -> Bool))?)
|
|
||||||
/// 刷新大小动画
|
|
||||||
func refreshAnimationOfAnimationView(animationView: SSAlertView, viewSize: CGSize, animated: Bool, completion:(((Bool) -> Void))?)
|
|
||||||
/// 拖拽隐藏动画,当是模态视图弹窗才有
|
|
||||||
func panToDimissTransilatePoint(point: CGPoint, panViewFrame: CGRect) -> (PanProgress, PanCancelProgress)
|
|
||||||
}
|
|
||||||
|
|
||||||
@ -1,46 +0,0 @@
|
|||||||
//
|
|
||||||
// SSAlertAnimationController.swift
|
|
||||||
// SSAlert
|
|
||||||
//
|
|
||||||
// Created by yangsq on 2021/8/13.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
|
|
||||||
class SSAlertAnimationController: UIViewController {
|
|
||||||
var isDimiss: Bool = false
|
|
||||||
var isHideStatusBar = false {
|
|
||||||
didSet {
|
|
||||||
setNeedsStatusBarAppearanceUpdate()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var isShowNavWhenViewWillDisappear = true
|
|
||||||
override func viewDidLoad() {
|
|
||||||
super.viewDidLoad()
|
|
||||||
view.backgroundColor = .clear
|
|
||||||
}
|
|
||||||
|
|
||||||
override var prefersStatusBarHidden: Bool {
|
|
||||||
return isHideStatusBar
|
|
||||||
}
|
|
||||||
|
|
||||||
override func viewWillAppear(_ animated: Bool) {
|
|
||||||
super.viewWillAppear(animated)
|
|
||||||
navigationController?.setNavigationBarHidden(true, animated: animated)
|
|
||||||
}
|
|
||||||
|
|
||||||
override func viewWillDisappear(_ animated: Bool) {
|
|
||||||
super.viewWillDisappear(animated)
|
|
||||||
if !isDimiss, isShowNavWhenViewWillDisappear {
|
|
||||||
navigationController?.setNavigationBarHidden(false, animated: animated)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) {
|
|
||||||
super.dismiss(animated: flag, completion: completion)
|
|
||||||
isDimiss = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@ -1,335 +0,0 @@
|
|||||||
//
|
|
||||||
// SSAlertCommonView.swift
|
|
||||||
// SSAlert
|
|
||||||
//
|
|
||||||
// Created by yangsq on 2021/8/13.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
public extension SSAlertAction {
|
|
||||||
enum Style {
|
|
||||||
case alert
|
|
||||||
case actionSheet
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public class SSAlertAction: NSObject {
|
|
||||||
public var title: String
|
|
||||||
public var titleFont = UIFont.systemFont(ofSize: 16)
|
|
||||||
public var titleColor: UIColor? = nil
|
|
||||||
public var height: CGFloat = 55
|
|
||||||
public var backgroundColor: UIColor? = nil
|
|
||||||
public private(set) var style: Style
|
|
||||||
private var addAction:(() -> Void)? = nil
|
|
||||||
public init(style: Style, title: String, addAction: (() -> Void)? = nil) {
|
|
||||||
self.style = style
|
|
||||||
self.title = title
|
|
||||||
self.addAction = addAction
|
|
||||||
if style == .alert {
|
|
||||||
height = 50
|
|
||||||
}
|
|
||||||
super.init()
|
|
||||||
}
|
|
||||||
func triggerAction() {
|
|
||||||
if let addAction = addAction {
|
|
||||||
addAction()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
extension SSAlertCommonView {
|
|
||||||
public static var alertMaxWidth = UIScreen.main.bounds.width * 0.7
|
|
||||||
public static var actionSheetMaxWidth = UIScreen.main.bounds.width
|
|
||||||
public static var buttonSpace: CGFloat = 0.5
|
|
||||||
|
|
||||||
public static var safeAreaBottomMargin: CGFloat {
|
|
||||||
if #available(iOS 11.0, *) {
|
|
||||||
return UIApplication.shared.keyWindow?.safeAreaInsets.bottom ?? 0
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class SSAlertCommonView: UIView {
|
|
||||||
|
|
||||||
public lazy var titleLabel: UILabel = {
|
|
||||||
let label = UILabel()
|
|
||||||
label.font = UIFont.systemFont(ofSize: 16)
|
|
||||||
label.numberOfLines = 0
|
|
||||||
label.textAlignment = .center
|
|
||||||
return label
|
|
||||||
}()
|
|
||||||
|
|
||||||
public lazy var messageLabel: UILabel = {
|
|
||||||
let label = UILabel()
|
|
||||||
label.font = UIFont.systemFont(ofSize: 14)
|
|
||||||
label.numberOfLines = 0
|
|
||||||
label.textAlignment = .center
|
|
||||||
return label
|
|
||||||
}()
|
|
||||||
|
|
||||||
public lazy var containerView: UIView = {
|
|
||||||
let view = UIView()
|
|
||||||
return view
|
|
||||||
}()
|
|
||||||
|
|
||||||
public lazy var buttonView: UIView = {
|
|
||||||
let view = UIView()
|
|
||||||
return view
|
|
||||||
}()
|
|
||||||
|
|
||||||
public var lineColor: UIColor? = nil {
|
|
||||||
didSet {
|
|
||||||
lineArray.forEach { lineView in
|
|
||||||
lineView.backgroundColor = lineColor
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public var lineEdgeInsets: UIEdgeInsets = .zero {
|
|
||||||
didSet {
|
|
||||||
refreshFrame()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public private(set) var style: SSAlertAction.Style
|
|
||||||
public private(set) var actions: [SSAlertAction]
|
|
||||||
public private(set) var title: String?
|
|
||||||
public private(set) var message: String?
|
|
||||||
|
|
||||||
private var buttonArray: [UIButton] = []
|
|
||||||
private var lineArray: [UIView] = []
|
|
||||||
private var hasText = false
|
|
||||||
|
|
||||||
|
|
||||||
public init(title: String? = nil, message: String? = nil, style: SSAlertAction.Style, actions:[SSAlertAction] = []) {
|
|
||||||
self.title = title
|
|
||||||
self.message = message
|
|
||||||
self.style = style
|
|
||||||
self.actions = actions
|
|
||||||
super.init(frame: .zero)
|
|
||||||
makeUI()
|
|
||||||
}
|
|
||||||
|
|
||||||
private func makeUI() {
|
|
||||||
addSubview(containerView)
|
|
||||||
if let title = title, !title.isEmpty {
|
|
||||||
titleLabel.text = title
|
|
||||||
containerView.addSubview(titleLabel)
|
|
||||||
hasText = true
|
|
||||||
}
|
|
||||||
if let message = message, !message.isEmpty {
|
|
||||||
messageLabel.text = message
|
|
||||||
containerView.addSubview(messageLabel)
|
|
||||||
hasText = true
|
|
||||||
}
|
|
||||||
|
|
||||||
if actions.count > 0 {
|
|
||||||
addSubview(buttonView)
|
|
||||||
actions.forEach { action in
|
|
||||||
let button = UIButton(type: .system)
|
|
||||||
button.setTitle(action.title, for: .normal)
|
|
||||||
button.backgroundColor = backgroundColor
|
|
||||||
if let backgroundColor = action.backgroundColor {
|
|
||||||
button.backgroundColor = backgroundColor
|
|
||||||
}
|
|
||||||
button.titleLabel?.font = action.titleFont
|
|
||||||
button.setTitleColor(action.titleColor, for: .normal)
|
|
||||||
button.addTarget(self, action: #selector(buttonAction(button:)), for: .touchUpInside)
|
|
||||||
buttonView.addSubview(button)
|
|
||||||
buttonArray.append(button)
|
|
||||||
var isAddLineView = false
|
|
||||||
if style == .actionSheet {
|
|
||||||
if hasText {
|
|
||||||
isAddLineView = true
|
|
||||||
} else {
|
|
||||||
if actions[actions.count - 1] != action {
|
|
||||||
isAddLineView = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if actions.count > 2 {
|
|
||||||
if hasText {
|
|
||||||
isAddLineView = true
|
|
||||||
} else {
|
|
||||||
if actions[actions.count - 1] != action {
|
|
||||||
isAddLineView = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if actions.count == 1 {
|
|
||||||
if hasText {
|
|
||||||
isAddLineView = true
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if hasText {
|
|
||||||
isAddLineView = true
|
|
||||||
} else {
|
|
||||||
if actions[actions.count - 1] != action {
|
|
||||||
isAddLineView = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if isAddLineView {
|
|
||||||
let linView = UIView()
|
|
||||||
linView.backgroundColor = UIColor.lightGray.withAlphaComponent(0.5)
|
|
||||||
buttonView.addSubview(linView)
|
|
||||||
lineArray.append(linView)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
refreshFrame()
|
|
||||||
}
|
|
||||||
|
|
||||||
private func refreshFrame() {
|
|
||||||
let space: CGFloat = 15
|
|
||||||
let viewMaxWidth = style == .alert ? SSAlertCommonView.alertMaxWidth : SSAlertCommonView.actionSheetMaxWidth
|
|
||||||
var height: CGFloat = 0
|
|
||||||
let width = viewMaxWidth
|
|
||||||
let containerWidth = viewMaxWidth - space * 2
|
|
||||||
var originY: CGFloat = 0
|
|
||||||
var cotainerOriginY: CGFloat = 0
|
|
||||||
if titleLabel.superview != nil {
|
|
||||||
let titleSize = titleLabel.sizeThatFits(CGSize(width: containerWidth, height: CGFloat(MAXFLOAT)))
|
|
||||||
titleLabel.ss_origin = CGPoint(x: 0, y: cotainerOriginY)
|
|
||||||
titleLabel.ss_size = CGSize(width: containerWidth, height: titleSize.height)
|
|
||||||
height += (titleSize.height + space)
|
|
||||||
cotainerOriginY += (space + titleSize.height)
|
|
||||||
originY = space
|
|
||||||
}
|
|
||||||
|
|
||||||
if messageLabel.superview != nil {
|
|
||||||
let messageSize = messageLabel.sizeThatFits(CGSize(width: containerWidth, height: CGFloat(MAXFLOAT)))
|
|
||||||
messageLabel.ss_origin = CGPoint(x: 0, y: cotainerOriginY)
|
|
||||||
messageLabel.ss_size = CGSize(width: containerWidth, height: messageSize.height)
|
|
||||||
height += (messageSize.height + space)
|
|
||||||
cotainerOriginY += (space + messageSize.height)
|
|
||||||
originY = space
|
|
||||||
}
|
|
||||||
|
|
||||||
containerView.ss_origin = CGPoint(x: space, y: originY)
|
|
||||||
containerView.ss_size = CGSize(width: containerWidth, height: height)
|
|
||||||
originY += height
|
|
||||||
|
|
||||||
if !buttonArray.isEmpty {
|
|
||||||
let buttonSpace: CGFloat = SSAlertCommonView.buttonSpace
|
|
||||||
var buttonTotalHeight: CGFloat = 0
|
|
||||||
var buttonTopY: CGFloat = 0
|
|
||||||
if style == .actionSheet {
|
|
||||||
for index in 0..<buttonArray.count {
|
|
||||||
let action = actions[index]
|
|
||||||
let button = buttonArray[index]
|
|
||||||
if lineArray.count == buttonArray.count {
|
|
||||||
let lineView = lineArray[index]
|
|
||||||
lineView.frame = CGRect(x: lineEdgeInsets.left, y: buttonTopY, width: width - lineEdgeInsets.left - lineEdgeInsets.right, height: buttonSpace)
|
|
||||||
buttonTopY += buttonSpace
|
|
||||||
}
|
|
||||||
|
|
||||||
if lineArray.count == buttonArray.count - 1 {
|
|
||||||
if index < lineArray.count {
|
|
||||||
let lineView = lineArray[index]
|
|
||||||
lineView.frame = CGRect(x: lineEdgeInsets.left, y: buttonTopY + action.height, width: width - lineEdgeInsets.left - lineEdgeInsets.right, height: buttonSpace)
|
|
||||||
buttonTopY += buttonSpace
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if index == buttonArray.count - 1 {
|
|
||||||
button.ss_size = CGSize(width: width, height: action.height + SSAlertCommonView.safeAreaBottomMargin)
|
|
||||||
button.titleEdgeInsets = UIEdgeInsets(top: -SSAlertCommonView.safeAreaBottomMargin, left: 0, bottom: 0, right: 0)
|
|
||||||
} else {
|
|
||||||
button.ss_size = CGSize(width: width, height: action.height)
|
|
||||||
}
|
|
||||||
button.ss_origin = CGPoint(x: 0, y: buttonTopY)
|
|
||||||
buttonTopY += button.ss_h
|
|
||||||
}
|
|
||||||
buttonTotalHeight = buttonTopY
|
|
||||||
}
|
|
||||||
|
|
||||||
if style == .alert {
|
|
||||||
if buttonArray.count > 2 {
|
|
||||||
buttonArray.forEach { button in
|
|
||||||
let index = buttonArray.firstIndex(of: button)
|
|
||||||
let action = actions[index!]
|
|
||||||
|
|
||||||
if lineArray.count == buttonArray.count {
|
|
||||||
let lineView = lineArray[index!]
|
|
||||||
lineView.frame = CGRect(x: lineEdgeInsets.left, y: buttonTopY, width: width - lineEdgeInsets.left - lineEdgeInsets.right, height: buttonSpace)
|
|
||||||
buttonTopY += buttonSpace
|
|
||||||
}
|
|
||||||
if lineArray.count == buttonArray.count - 1 {
|
|
||||||
if index! < lineArray.count {
|
|
||||||
let lineView = lineArray[index!]
|
|
||||||
lineView.frame = CGRect(x: lineEdgeInsets.left, y: buttonTopY + action.height, width: width - lineEdgeInsets.left - lineEdgeInsets.right, height: buttonSpace)
|
|
||||||
buttonTopY += buttonSpace
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
button.ss_origin = CGPoint(x: 0, y: buttonTopY)
|
|
||||||
button.ss_size = CGSize(width: width, height: action.height)
|
|
||||||
buttonTopY += action.height
|
|
||||||
}
|
|
||||||
buttonTotalHeight = buttonTopY
|
|
||||||
} else {
|
|
||||||
if buttonArray.count == 1 {
|
|
||||||
let button = buttonArray.first!
|
|
||||||
let action = actions.first!
|
|
||||||
if lineArray.count > 0 {
|
|
||||||
let lineView = lineArray.first!
|
|
||||||
lineView.frame = CGRect(x: lineEdgeInsets.left, y: buttonTopY, width: width - lineEdgeInsets.left - lineEdgeInsets.right, height: buttonSpace)
|
|
||||||
buttonTopY += buttonSpace
|
|
||||||
}
|
|
||||||
button.ss_origin = CGPoint(x: 0, y: buttonTopY)
|
|
||||||
button.ss_size = CGSize(width: width, height: action.height)
|
|
||||||
buttonTotalHeight = buttonTopY
|
|
||||||
} else {
|
|
||||||
let button1 = buttonArray.first!
|
|
||||||
let button2 = buttonArray.last!
|
|
||||||
let action1 = actions.first!
|
|
||||||
let action2 = actions.last!
|
|
||||||
let maxButtonHeight = max(action1.height, action2.height)
|
|
||||||
if hasText {
|
|
||||||
let lineView1 = lineArray.first!
|
|
||||||
lineView1.frame = CGRect(x: 0, y: buttonTopY, width: width, height: buttonSpace)
|
|
||||||
buttonTopY += buttonSpace
|
|
||||||
}
|
|
||||||
|
|
||||||
button1.ss_origin = CGPoint(x: 0, y: buttonTopY)
|
|
||||||
button1.ss_size = CGSize(width: width / 2 - buttonSpace / 2, height: maxButtonHeight)
|
|
||||||
|
|
||||||
|
|
||||||
button2.ss_origin = CGPoint(x: button1.frame.maxX + buttonSpace, y: buttonTopY)
|
|
||||||
button2.ss_size = CGSize(width: width / 2 - buttonSpace / 2, height: maxButtonHeight)
|
|
||||||
|
|
||||||
let lineView2 = lineArray.last!
|
|
||||||
lineView2.frame = CGRect(x: button1.frame.maxX, y: buttonSpace, width: buttonSpace, height: button1.ss_h)
|
|
||||||
buttonTopY += maxButtonHeight
|
|
||||||
}
|
|
||||||
buttonTotalHeight = buttonTopY
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
buttonView.ss_origin = CGPoint(x: 0, y: originY)
|
|
||||||
buttonView.ss_size = CGSize(width: width, height: buttonTotalHeight)
|
|
||||||
height += buttonTotalHeight + (originY > 0 ? space : 0)
|
|
||||||
|
|
||||||
}
|
|
||||||
self.ss_size = CGSize(width: width, height: height)
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc private func buttonAction(button: UIButton) {
|
|
||||||
let index = buttonArray.firstIndex(of: button)
|
|
||||||
let action = actions[index!]
|
|
||||||
action.triggerAction()
|
|
||||||
}
|
|
||||||
|
|
||||||
required init?(coder: NSCoder) {
|
|
||||||
fatalError("init(coder:) has not been implemented")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,223 +0,0 @@
|
|||||||
//
|
|
||||||
// SSAlertDefaultAnmation.swift
|
|
||||||
// SSAlert
|
|
||||||
//
|
|
||||||
// Created by yangsq on 2021/8/13.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
extension SSAlertDefaultAnmation {
|
|
||||||
public enum State {
|
|
||||||
case fromTop, fromLeft, fromBottom, fromRight, fromCenter
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
open class SSAlertDefaultAnmation: NSObject {
|
|
||||||
public var state: State
|
|
||||||
/// 中心点偏移原来位置位移
|
|
||||||
public var centerOffset: CGPoint = .zero
|
|
||||||
/// 动画的时间
|
|
||||||
public var duration: TimeInterval = 0.3
|
|
||||||
/// 展示动画是否开启弹簧效果
|
|
||||||
public var isSpringShowAnimation: Bool = true
|
|
||||||
/// 滑动消失距离触发
|
|
||||||
public var panDimissProgress: CGFloat = 0.4
|
|
||||||
public var usingSpringWithDamping: CGFloat = 0.8
|
|
||||||
public var initialSpringVelocity: CGFloat = 0.5
|
|
||||||
private weak var animationView: SSAlertView? = nil
|
|
||||||
public init(state: State) {
|
|
||||||
self.state = state
|
|
||||||
super.init()
|
|
||||||
}
|
|
||||||
|
|
||||||
private func animation(animated: Bool, isSpringAnimation: Bool = true, animations: @escaping (() -> Void), completion: ((Bool) -> Void)? = nil) {
|
|
||||||
if animated {
|
|
||||||
if isSpringAnimation {
|
|
||||||
UIView.animate(withDuration: animationDuration(),
|
|
||||||
delay: 0,
|
|
||||||
usingSpringWithDamping: usingSpringWithDamping,
|
|
||||||
initialSpringVelocity: initialSpringVelocity,
|
|
||||||
options: [],
|
|
||||||
animations: animations,
|
|
||||||
completion: completion)
|
|
||||||
} else {
|
|
||||||
UIView.animate(withDuration: animationDuration(),
|
|
||||||
animations: animations,
|
|
||||||
completion: completion)
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
animations()
|
|
||||||
if let completion = completion {
|
|
||||||
completion(true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public func setCenterOffset(center offset: CGPoint, duration: TimeInterval = 0.3, animated: Bool = true, completion:((Bool) -> Void)?) {
|
|
||||||
self.duration = duration
|
|
||||||
self.animation(animated: animated, animations: {
|
|
||||||
self.animationView?.transform = CGAffineTransform.init(translationX: offset.x, y: offset.y)
|
|
||||||
}, completion: completion)
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension SSAlertDefaultAnmation: SSAlertAnimation {
|
|
||||||
public func animationDuration() -> TimeInterval {
|
|
||||||
return duration
|
|
||||||
}
|
|
||||||
|
|
||||||
public func showAnimationOfAnimationView(animationView: SSAlertView, viewSize: CGSize, animated: Bool, completion: (((Bool) -> Void))?) {
|
|
||||||
guard let animationSuperView = animationView.superview else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
self.animationView = animationView
|
|
||||||
animationView.alpha = 1
|
|
||||||
animationView.ss_size = viewSize
|
|
||||||
switch state {
|
|
||||||
case .fromBottom:
|
|
||||||
animationView.ss_centerX = animationSuperView.ss_w / 2 + centerOffset.x
|
|
||||||
animationView.ss_y = animationSuperView.ss_h
|
|
||||||
animation(animated: animated, isSpringAnimation: isSpringShowAnimation, animations: { [weak self] in guard let self = self else { return }
|
|
||||||
animationView.transform = CGAffineTransform.init(translationX: 0, y: -animationView.ss_h + self.centerOffset.y)
|
|
||||||
animationView.backgroundMask?.alpha = 1;
|
|
||||||
}, completion: completion)
|
|
||||||
|
|
||||||
case .fromTop:
|
|
||||||
animationView.ss_centerX = animationSuperView.ss_w / 2 + centerOffset.x
|
|
||||||
animationView.ss_y = -animationView.ss_h
|
|
||||||
animation(animated: animated, isSpringAnimation: isSpringShowAnimation, animations: { [weak self] in guard let self = self else { return }
|
|
||||||
animationView.transform = CGAffineTransform.init(translationX: 0, y: animationView.ss_h + self.centerOffset.y)
|
|
||||||
animationView.backgroundMask?.alpha = 1;
|
|
||||||
}, completion: completion)
|
|
||||||
|
|
||||||
case .fromLeft:
|
|
||||||
animationView.ss_centerY = animationSuperView.ss_h / 2 + centerOffset.y
|
|
||||||
animationView.ss_x = -animationView.ss_w
|
|
||||||
animation(animated: animated, isSpringAnimation: isSpringShowAnimation, animations: {
|
|
||||||
animationView.transform = CGAffineTransform.init(translationX: animationView.ss_w + self.centerOffset.x, y: 0)
|
|
||||||
animationView.backgroundMask?.alpha = 1;
|
|
||||||
}, completion: completion)
|
|
||||||
|
|
||||||
case .fromRight:
|
|
||||||
animationView.ss_centerY = animationSuperView.ss_h / 2 + centerOffset.y
|
|
||||||
animationView.ss_x = animationSuperView.ss_w
|
|
||||||
animation(animated: animated, isSpringAnimation: isSpringShowAnimation, animations: {
|
|
||||||
animationView.transform = CGAffineTransform.init(translationX: -animationView.ss_w + self.centerOffset.x, y: 0)
|
|
||||||
animationView.backgroundMask?.alpha = 1;
|
|
||||||
}, completion: completion)
|
|
||||||
|
|
||||||
case .fromCenter:
|
|
||||||
animationView.ss_centerX = animationSuperView.ss_w / 2 + centerOffset.x
|
|
||||||
animationView.ss_centerY = animationSuperView.ss_h / 2 + centerOffset.y
|
|
||||||
animationView.transform = CGAffineTransform.init(scaleX: 1.5, y: 1.5)
|
|
||||||
animationView.alpha = 0
|
|
||||||
animation(animated: animated, isSpringAnimation: isSpringShowAnimation, animations: {
|
|
||||||
animationView.alpha = 1
|
|
||||||
animationView.transform = CGAffineTransform.identity
|
|
||||||
animationView.backgroundMask?.alpha = 1;
|
|
||||||
}, completion: completion)
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public func hideAnimationOfAnimationView(animationView: SSAlertView, viewSize: CGSize, animated: Bool, completion: (((Bool) -> Bool))?) {
|
|
||||||
self.animationView = animationView
|
|
||||||
animation(animated: animated, isSpringAnimation: false, animations: {
|
|
||||||
if self.state == .fromCenter {
|
|
||||||
animationView.alpha = 0
|
|
||||||
} else {
|
|
||||||
animationView.transform = CGAffineTransform.identity
|
|
||||||
}
|
|
||||||
animationView.backgroundMask?.alpha = 0
|
|
||||||
}, completion: {
|
|
||||||
finished in
|
|
||||||
var isCancel = false
|
|
||||||
if let completion = completion {
|
|
||||||
isCancel = completion(finished)
|
|
||||||
}
|
|
||||||
if !isCancel {
|
|
||||||
animationView.removeFromSuperview()
|
|
||||||
animationView.backgroundMask?.removeFromSuperview()
|
|
||||||
self.animationView = nil
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
public func refreshAnimationOfAnimationView(animationView: SSAlertView, viewSize: CGSize, animated: Bool, completion: (((Bool) -> Void))?) {
|
|
||||||
guard let animationSuperView = animationView.superview else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
self.animationView = animationView
|
|
||||||
animationView.alpha = 1
|
|
||||||
animationView.ss_size = viewSize
|
|
||||||
switch state {
|
|
||||||
case .fromBottom:
|
|
||||||
animationView.ss_centerX = animationSuperView.ss_w / 2.0 + centerOffset.x
|
|
||||||
animation(animated: animated, animations: {
|
|
||||||
animationView.ss_y = animationSuperView.ss_h - viewSize.height + self.centerOffset.y
|
|
||||||
}, completion: completion)
|
|
||||||
|
|
||||||
case .fromTop:
|
|
||||||
animationView.ss_centerX = animationSuperView.ss_w / 2.0 + centerOffset.x
|
|
||||||
animation(animated: animated, animations: {
|
|
||||||
animationView.ss_y = self.centerOffset.y
|
|
||||||
}, completion: completion)
|
|
||||||
|
|
||||||
case .fromLeft:
|
|
||||||
animationView.ss_centerY = animationSuperView.ss_h / 2.0 + centerOffset.y
|
|
||||||
animation(animated: animated, animations: {
|
|
||||||
animationView.ss_x = self.centerOffset.x
|
|
||||||
}, completion: completion)
|
|
||||||
|
|
||||||
case .fromRight:
|
|
||||||
animationView.ss_centerY = animationSuperView.ss_h / 2.0 + centerOffset.y
|
|
||||||
animation(animated: animated, animations: {
|
|
||||||
animationView.ss_x = animationSuperView.ss_w - viewSize.width + self.centerOffset.x
|
|
||||||
}, completion: completion)
|
|
||||||
|
|
||||||
case .fromCenter:
|
|
||||||
animationView.alpha = 0
|
|
||||||
animationView.ss_centerX = animationSuperView.ss_w / 2 + centerOffset.x
|
|
||||||
animationView.ss_centerY = animationSuperView.ss_h / 2 + centerOffset.y
|
|
||||||
animationView.transform = CGAffineTransform.init(scaleX: 1.5, y: 1.5)
|
|
||||||
animation(animated: animated, animations: {
|
|
||||||
animationView.alpha = 1
|
|
||||||
animationView.transform = CGAffineTransform.identity
|
|
||||||
}, completion: completion)
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public func panToDimissTransilatePoint(point: CGPoint, panViewFrame: CGRect) -> (PanProgress, PanCancelProgress) {
|
|
||||||
var progress: CGFloat = 0
|
|
||||||
switch state {
|
|
||||||
case .fromTop:
|
|
||||||
if point.y <= 0 {
|
|
||||||
progress = abs(point.y / panViewFrame.height)
|
|
||||||
}
|
|
||||||
case .fromBottom:
|
|
||||||
if point.y >= 0 {
|
|
||||||
progress = abs(point.y / panViewFrame.height)
|
|
||||||
}
|
|
||||||
|
|
||||||
case .fromLeft:
|
|
||||||
if point.x <= 0 {
|
|
||||||
progress = abs(point.x / panViewFrame.width)
|
|
||||||
}
|
|
||||||
|
|
||||||
case .fromRight:
|
|
||||||
if point.x >= 0 {
|
|
||||||
progress = abs(point.x / panViewFrame.width)
|
|
||||||
}
|
|
||||||
case .fromCenter:
|
|
||||||
progress = abs(point.y / panViewFrame.height)
|
|
||||||
}
|
|
||||||
return (progress, panDimissProgress)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@ -1,222 +0,0 @@
|
|||||||
//
|
|
||||||
// SSAlertPresentAnimation.swift
|
|
||||||
// SSAlert
|
|
||||||
//
|
|
||||||
// Created by yangsq on 2021/8/13.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
typealias PanDimissAnimation = (CGPoint, CGRect) -> (PanProgress, PanCancelProgress)
|
|
||||||
typealias DimissAnimation = (DimissIsCancel) -> Void
|
|
||||||
|
|
||||||
class SSCustomInteractiveAnimation: UIPercentDrivenInteractiveTransition {
|
|
||||||
private(set) var panGestureRecognizer: UIPanGestureRecognizer
|
|
||||||
private var panDimissAnimation: PanDimissAnimation?
|
|
||||||
private var dimissAnimation: DimissAnimation?
|
|
||||||
|
|
||||||
init(panGestureRecognizer: UIPanGestureRecognizer, panDimissAnimation: PanDimissAnimation?, dimissAnimation: DimissAnimation?) {
|
|
||||||
self.panGestureRecognizer = panGestureRecognizer
|
|
||||||
self.panDimissAnimation = panDimissAnimation
|
|
||||||
self.dimissAnimation = dimissAnimation
|
|
||||||
super.init()
|
|
||||||
self.completionSpeed = 0.2
|
|
||||||
panGestureRecognizer.addTarget(self, action: #selector(panAction(pan:)))
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc private func panAction(pan: UIPanGestureRecognizer) {
|
|
||||||
guard let view = pan.view else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
let poin = pan.translation(in: view)
|
|
||||||
var progress: CGFloat = 0
|
|
||||||
var cancelProgress: CGFloat = 0.4
|
|
||||||
if let panDimissAnimation = self.panDimissAnimation {
|
|
||||||
let (tProgress, tCancelProgress) = panDimissAnimation(poin, pan.view!.frame)
|
|
||||||
progress = tProgress
|
|
||||||
cancelProgress = tCancelProgress
|
|
||||||
}
|
|
||||||
switch pan.state {
|
|
||||||
case .began:
|
|
||||||
pan.setTranslation(CGPoint(x: 0, y: 0), in: view)
|
|
||||||
case .changed:
|
|
||||||
update(progress)
|
|
||||||
case .cancelled:
|
|
||||||
cancel()
|
|
||||||
case .ended:
|
|
||||||
if progress > cancelProgress {
|
|
||||||
finish()
|
|
||||||
dimissAnimation?(false)
|
|
||||||
} else {
|
|
||||||
cancel()
|
|
||||||
dimissAnimation?(true)
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override func startInteractiveTransition(_ transitionContext: UIViewControllerContextTransitioning) {
|
|
||||||
super.startInteractiveTransition(transitionContext)
|
|
||||||
}
|
|
||||||
|
|
||||||
deinit {
|
|
||||||
panGestureRecognizer.removeTarget(self, action: #selector(panAction(pan:)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class SSAlertPresentAnimation: NSObject {
|
|
||||||
typealias AnimationCompletion = () -> Void
|
|
||||||
var duration: TimeInterval = 0.35
|
|
||||||
private var showAnimation: AnimationCompletion? = nil
|
|
||||||
private var hideAnimation: AnimationCompletion? = nil
|
|
||||||
private var endCompletion: AnimationCompletion? = nil
|
|
||||||
private var panDimissAnimation: PanDimissAnimation? = nil
|
|
||||||
private var dimissAnimation: DimissAnimation? = nil
|
|
||||||
|
|
||||||
private var transitionContext: UIViewControllerContextTransitioning?
|
|
||||||
private weak var nav: UINavigationController?
|
|
||||||
private weak var animationView: UIView?
|
|
||||||
private var panGestureRecognizer: UIPanGestureRecognizer?
|
|
||||||
|
|
||||||
init(navigationViewController: UINavigationController, animationView: UIView, canPanDimiss: Bool = true) {
|
|
||||||
self.nav = navigationViewController
|
|
||||||
self.animationView = animationView
|
|
||||||
super.init()
|
|
||||||
if canPanDimiss {
|
|
||||||
let pan = UIPanGestureRecognizer(target: self, action: #selector(panAction(pan:)))
|
|
||||||
pan.maximumNumberOfTouches = 1
|
|
||||||
animationView.addGestureRecognizer(pan)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc func panAction(pan: UIPanGestureRecognizer) {
|
|
||||||
if pan.state == .began {
|
|
||||||
panGestureRecognizer = pan
|
|
||||||
nav?.viewControllers[0].dismiss(animated: true, completion: {
|
|
||||||
[weak self] in guard let self = self else { return }
|
|
||||||
self.panGestureRecognizer = nil
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func animationCompletion(showAnimation: AnimationCompletion? = nil,
|
|
||||||
hideAnimation: AnimationCompletion? = nil,
|
|
||||||
endCompletion: AnimationCompletion? = nil,
|
|
||||||
panDimissAnimation: PanDimissAnimation? = nil,
|
|
||||||
dimissAnimation: DimissAnimation? = nil) {
|
|
||||||
if showAnimation != nil {
|
|
||||||
self.showAnimation = showAnimation
|
|
||||||
}
|
|
||||||
if hideAnimation != nil {
|
|
||||||
self.hideAnimation = hideAnimation
|
|
||||||
}
|
|
||||||
|
|
||||||
if endCompletion != nil {
|
|
||||||
self.endCompletion = endCompletion
|
|
||||||
}
|
|
||||||
if panDimissAnimation != nil {
|
|
||||||
self.panDimissAnimation = panDimissAnimation
|
|
||||||
}
|
|
||||||
if panDimissAnimation != nil {
|
|
||||||
self.panDimissAnimation = panDimissAnimation
|
|
||||||
}
|
|
||||||
|
|
||||||
if dimissAnimation != nil {
|
|
||||||
self.dimissAnimation = dimissAnimation
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@discardableResult
|
|
||||||
func setCompleteTransitionIsHide(isHide: Bool) -> Bool {
|
|
||||||
if let transitionContext = transitionContext {
|
|
||||||
let isCancelled = transitionContext.transitionWasCancelled
|
|
||||||
if !isCancelled {
|
|
||||||
if isHide {
|
|
||||||
transitionContext.containerView.subviews.forEach { subView in
|
|
||||||
subView.removeFromSuperview()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
transitionContext.completeTransition(!isCancelled)
|
|
||||||
return isCancelled
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension SSAlertPresentAnimation: UIViewControllerTransitioningDelegate {
|
|
||||||
func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
|
|
||||||
return self
|
|
||||||
}
|
|
||||||
func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
|
|
||||||
return self
|
|
||||||
}
|
|
||||||
|
|
||||||
func interactionControllerForDismissal(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
|
|
||||||
if self.panGestureRecognizer != nil {
|
|
||||||
return SSCustomInteractiveAnimation(panGestureRecognizer: self.panGestureRecognizer!, panDimissAnimation: self.panDimissAnimation, dimissAnimation: dimissAnimation)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
func interactionControllerForPresentation(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
|
|
||||||
if self.panGestureRecognizer != nil {
|
|
||||||
|
|
||||||
return SSCustomInteractiveAnimation(panGestureRecognizer: self.panGestureRecognizer!, panDimissAnimation: self.panDimissAnimation, dimissAnimation: dimissAnimation)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension SSAlertPresentAnimation: UIViewControllerAnimatedTransitioning {
|
|
||||||
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
|
|
||||||
return self.duration
|
|
||||||
}
|
|
||||||
|
|
||||||
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
|
|
||||||
self.transitionContext = transitionContext
|
|
||||||
let fromVC = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from)
|
|
||||||
let toVC = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to)
|
|
||||||
let containerView = transitionContext.containerView
|
|
||||||
|
|
||||||
if let fromVC = fromVC, let toVC = toVC {
|
|
||||||
var fromView = fromVC.view
|
|
||||||
var toView = toVC.view
|
|
||||||
if transitionContext.responds(to: #selector(UIViewControllerContextTransitioning.view(forKey:))) {
|
|
||||||
fromView = transitionContext.view(forKey: UITransitionContextViewKey.from)
|
|
||||||
toView = transitionContext.view(forKey: UITransitionContextViewKey.to)
|
|
||||||
}
|
|
||||||
let isPresenting = toVC.presentingViewController == fromVC
|
|
||||||
if isPresenting {
|
|
||||||
if let toView = toView {
|
|
||||||
containerView.addSubview(toView)
|
|
||||||
|
|
||||||
}
|
|
||||||
if let showAnimation = self.showAnimation {
|
|
||||||
showAnimation()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if let toView = toView, let fromView = fromView {
|
|
||||||
containerView.insertSubview(toView, belowSubview: fromView)
|
|
||||||
|
|
||||||
}
|
|
||||||
if let hideAnimation = self.hideAnimation {
|
|
||||||
hideAnimation()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
func animationEnded(_ transitionCompleted: Bool) {
|
|
||||||
if let endCompletion = self.endCompletion, transitionCompleted, let transitionContext = transitionContext, transitionContext.isInteractive {
|
|
||||||
endCompletion()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,312 +0,0 @@
|
|||||||
//
|
|
||||||
// SSAlertView.swift
|
|
||||||
// SSAlert
|
|
||||||
//
|
|
||||||
// Created by yangsq on 2021/8/13.
|
|
||||||
//
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
|
|
||||||
extension SSAlertView {
|
|
||||||
public enum BackgroundMaskType {
|
|
||||||
/// 默认没有遮罩
|
|
||||||
case none
|
|
||||||
|
|
||||||
/// 透明遮罩
|
|
||||||
case clear
|
|
||||||
|
|
||||||
/// 黑色遮罩
|
|
||||||
case black
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fileprivate extension SSAlertView {
|
|
||||||
typealias HideCompletion = () -> Void
|
|
||||||
class HideCompletionData: NSObject {
|
|
||||||
var completion: HideCompletion?
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
open class SSAlertView: UIView {
|
|
||||||
/// customView 的内间距
|
|
||||||
public var customEdgeInsets: UIEdgeInsets = .zero {
|
|
||||||
didSet {
|
|
||||||
setNeedsDisplay()
|
|
||||||
layoutIfNeeded()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 展示和隐藏动画设置
|
|
||||||
public var animation: SSAlertAnimation
|
|
||||||
|
|
||||||
///遮罩视图
|
|
||||||
public private(set) var backgroundMask: UIView?
|
|
||||||
|
|
||||||
/// 是否点击遮罩隐藏,默认点击会隐藏
|
|
||||||
public var canTouchMaskHide: Bool = true
|
|
||||||
/// 点击遮罩隐藏是否开启动画,默认nil,跟随展示的时候设置是否开启动画
|
|
||||||
public var isTouchMaskHideAnimated: Bool? = nil
|
|
||||||
|
|
||||||
/// 模态视图弹窗才有
|
|
||||||
public private(set) var navigationController: UINavigationController?
|
|
||||||
|
|
||||||
public var isHideStatusBar = false {
|
|
||||||
didSet {
|
|
||||||
self.toViewContrller?.isHideStatusBar = isHideStatusBar
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public var isShowNavWhenViewWillDisappear = true {
|
|
||||||
didSet {
|
|
||||||
self.toViewContrller?.isShowNavWhenViewWillDisappear = isShowNavWhenViewWillDisappear
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private var onView: UIView
|
|
||||||
public private(set) var maskType: BackgroundMaskType
|
|
||||||
public private(set) var customView: UIView
|
|
||||||
private weak var fromViewController: UIViewController?
|
|
||||||
private weak var toViewContrller: SSAlertAnimationController?
|
|
||||||
private var presentAnimation: SSAlertPresentAnimation?
|
|
||||||
private var canPanDimiss: Bool = false
|
|
||||||
private var hideCompletions = [HideCompletionData]()
|
|
||||||
/// 初始化(普通视图弹窗)
|
|
||||||
public init(customView: UIView,
|
|
||||||
onView: UIView,
|
|
||||||
animation: SSAlertAnimation = SSAlertDefaultAnmation(state: .fromCenter),
|
|
||||||
maskType: BackgroundMaskType = .black) {
|
|
||||||
self.animation = animation
|
|
||||||
self.onView = onView
|
|
||||||
self.maskType = maskType
|
|
||||||
self.customView = customView
|
|
||||||
super.init(frame: .zero)
|
|
||||||
makeUI()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 初始化(模态视图弹窗),canPanDimiss: 是否支持拖拽消息
|
|
||||||
public convenience init(customView: UIView,
|
|
||||||
fromViewController: UIViewController,
|
|
||||||
animation: SSAlertAnimation = SSAlertDefaultAnmation(state: .fromCenter),
|
|
||||||
navigationControllerClass: UINavigationController.Type = UINavigationController.self,
|
|
||||||
maskType: BackgroundMaskType = .black,
|
|
||||||
canPanDimiss: Bool = true) {
|
|
||||||
let animaionVC = SSAlertAnimationController()
|
|
||||||
self.init(customView: customView, onView: animaionVC.view, animation: animation, maskType: maskType)
|
|
||||||
animaionVC.isHideStatusBar = self.isHideStatusBar
|
|
||||||
if let superViewController = superViewController(view: customView), superViewController != animaionVC {
|
|
||||||
animaionVC.addChild(superViewController)
|
|
||||||
superViewController.didMove(toParent: animaionVC)
|
|
||||||
}
|
|
||||||
self.canPanDimiss = canPanDimiss
|
|
||||||
self.fromViewController = fromViewController
|
|
||||||
let nav = navigationControllerClass.init(rootViewController: animaionVC)
|
|
||||||
nav.modalPresentationStyle = .custom
|
|
||||||
self.navigationController = nav
|
|
||||||
self.canPanDimiss = canPanDimiss
|
|
||||||
let animation = SSAlertPresentAnimation(navigationViewController: nav, animationView: customView, canPanDimiss: canPanDimiss)
|
|
||||||
self.navigationController!.transitioningDelegate = animation
|
|
||||||
self.navigationController!.modalPresentationCapturesStatusBarAppearance = true
|
|
||||||
self.presentAnimation = animation
|
|
||||||
self.toViewContrller = animaionVC
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
open override func layoutSubviews() {
|
|
||||||
super.layoutSubviews()
|
|
||||||
backgroundMask?.frame = CGRect(x: 0, y: 0, width: onView.ss_w, height: onView.ss_h)
|
|
||||||
customView.ss_x = customEdgeInsets.left
|
|
||||||
customView.ss_y = customEdgeInsets.top
|
|
||||||
customView.ss_w = frame.width - customEdgeInsets.left - customEdgeInsets.right
|
|
||||||
customView.ss_h = frame.height - customEdgeInsets.top - customEdgeInsets.bottom
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private func makeUI() {
|
|
||||||
if maskType != .none {
|
|
||||||
let backgroundMask = UIButton()
|
|
||||||
backgroundMask.alpha = 0
|
|
||||||
onView.addSubview(backgroundMask)
|
|
||||||
backgroundMask.addTarget(self, action: #selector(backgroundMaskAction), for: .touchUpInside)
|
|
||||||
self.backgroundMask = backgroundMask
|
|
||||||
if maskType == .black {
|
|
||||||
backgroundMask.backgroundColor = UIColor.black.withAlphaComponent(0.3)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
addSubview(customView)
|
|
||||||
onView.addSubview(self)
|
|
||||||
}
|
|
||||||
/// 展示
|
|
||||||
public func show(animated: Bool = true, completion: (() -> Void)? = nil) {
|
|
||||||
if navigationController != nil {
|
|
||||||
presentView(animated: animated, completion: completion)
|
|
||||||
} else {
|
|
||||||
showView(animated: animated, completion: completion)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// 隐藏
|
|
||||||
public func hide(animated: Bool = true, completion: (() -> Void)? = nil) {
|
|
||||||
if navigationController != nil {
|
|
||||||
dimissView(animated: animated, completion: completion)
|
|
||||||
} else {
|
|
||||||
hideView(animated: animated, completion: completion)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 展示(普通视图弹窗)
|
|
||||||
/// - Parameter animated: 是否动画
|
|
||||||
private func showView(animated: Bool = true, completion: (() -> Void)? = nil) {
|
|
||||||
isTouchMaskHideAnimated = animated
|
|
||||||
var customSize = customView.systemLayoutSizeFitting(UIView.layoutFittingExpandedSize)
|
|
||||||
if customView.ss_size != .zero {
|
|
||||||
customSize = customView.ss_size
|
|
||||||
}
|
|
||||||
let size = CGSize(width: customSize.width + customEdgeInsets.left + customEdgeInsets.right, height: customSize.height + customEdgeInsets.top + customEdgeInsets.bottom)
|
|
||||||
animation.showAnimationOfAnimationView(animationView: self, viewSize: size, animated: animated, completion: { _ in completion?() })
|
|
||||||
}
|
|
||||||
/// 隐藏(普通视图弹窗)
|
|
||||||
/// - Parameter animated: 是否动画
|
|
||||||
private func hideView(animated: Bool = true, completion: (() -> Void)? = nil) {
|
|
||||||
var customSize = customView.systemLayoutSizeFitting(UIView.layoutFittingExpandedSize)
|
|
||||||
if customView.ss_size != .zero {
|
|
||||||
customSize = customView.ss_size
|
|
||||||
}
|
|
||||||
let size = CGSize(width: customSize.width + customEdgeInsets.left + customEdgeInsets.right, height: customSize.height + customEdgeInsets.top + customEdgeInsets.bottom)
|
|
||||||
animation.hideAnimationOfAnimationView(animationView: self, viewSize: size, animated: animated) { [weak self] _ in guard let self = self else { return false }
|
|
||||||
completion?()
|
|
||||||
|
|
||||||
self.hideCompletions.forEach { hideC in
|
|
||||||
hideC.completion?()
|
|
||||||
hideC.completion = nil
|
|
||||||
self.hideCompletions.removeAll(where: {$0 == hideC})
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
/// 展示(模态视图弹窗)
|
|
||||||
/// - Parameter animated: 是否动画
|
|
||||||
private func presentView(animated: Bool = true, completion: (() -> Void)? = nil) {
|
|
||||||
isTouchMaskHideAnimated = animated
|
|
||||||
var customSize = customView.systemLayoutSizeFitting(UIView.layoutFittingExpandedSize)
|
|
||||||
if customView.ss_size != .zero {
|
|
||||||
customSize = customView.ss_size
|
|
||||||
}
|
|
||||||
let size = CGSize(width: customSize.width + customEdgeInsets.left + customEdgeInsets.right, height: customSize.height + customEdgeInsets.top + customEdgeInsets.bottom)
|
|
||||||
presentAnimation?.animationCompletion(showAnimation: {
|
|
||||||
[weak self] in guard let self = self else { return }
|
|
||||||
self.animation.showAnimationOfAnimationView(animationView: self, viewSize: size, animated: animated) { _ in
|
|
||||||
self.presentAnimation?.setCompleteTransitionIsHide(isHide: false)
|
|
||||||
completion?()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
self.presentAnimation?.duration = animation.animationDuration()
|
|
||||||
///存在的问题: app当前显示的是弹窗界面,默认presnent出来的视图且modalPresentationStyle不是CurrentContext这这种,这个视图的presentingVC默认根控制器,例如存在tabbarVC的话就是tabbarVC,这个会造成一个问题:当这个视图dimiss的时候,会把所有的presentedVC都dimiss(其实调用dimiss的时候是调用当前视图的presentigngVC去dimiss,遵循随创建随释放)
|
|
||||||
/// 这个通过设置 definesPresentationContext = true,modalPresentationStyle = UIModalPresentationOverCurrentContext,这样当前视图的presentigngVC为 self.navigationController,
|
|
||||||
/// UIModalPresentationOverCurrentContext和 UIModalPresentationCurrentContext的区别是弹出的界面,前者不会把底部的视图stact移除,后者则会。
|
|
||||||
self.navigationController?.definesPresentationContext = true
|
|
||||||
self.navigationController?.modalPresentationStyle = .overCurrentContext
|
|
||||||
self.fromViewController?.present(self.navigationController!, animated: true, completion: nil)
|
|
||||||
if canPanDimiss {
|
|
||||||
dimissViewDragToHideAnimation(animated: animated)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// 隐藏(模态视图弹窗)
|
|
||||||
/// - Parameter animated: 是否动画
|
|
||||||
private func dimissView(animated: Bool = true, completion: (() -> Void)? = nil) {
|
|
||||||
var customSize = customView.systemLayoutSizeFitting(UIView.layoutFittingExpandedSize)
|
|
||||||
if customView.ss_size != .zero {
|
|
||||||
customSize = customView.ss_size
|
|
||||||
}
|
|
||||||
let size = CGSize(width: customSize.width + customEdgeInsets.left + customEdgeInsets.right, height: customSize.height + customEdgeInsets.top + customEdgeInsets.bottom)
|
|
||||||
presentAnimation?.animationCompletion(hideAnimation: {
|
|
||||||
[weak self] in guard let self = self else { return }
|
|
||||||
self.animation.hideAnimationOfAnimationView(animationView: self, viewSize: size, animated: animated) { _ in
|
|
||||||
return self.presentAnimation!.setCompleteTransitionIsHide(isHide: true)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
self.presentAnimation?.duration = animation.animationDuration()
|
|
||||||
self.toViewContrller?.dismiss(animated: animated, completion: {
|
|
||||||
[weak self] in guard let self = self else { return }
|
|
||||||
completion?()
|
|
||||||
self.hideCompletions.forEach { hideC in
|
|
||||||
hideC.completion?()
|
|
||||||
hideC.completion = nil
|
|
||||||
self.hideCompletions.removeAll(where: {$0 == hideC})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
private func dimissViewDragToHideAnimation(animated: Bool) {
|
|
||||||
var customSize = customView.systemLayoutSizeFitting(UIView.layoutFittingExpandedSize)
|
|
||||||
if customView.ss_size != .zero {
|
|
||||||
customSize = customView.ss_size
|
|
||||||
}
|
|
||||||
let size = CGSize(width: customSize.width + customEdgeInsets.left + customEdgeInsets.right, height: customSize.height + customEdgeInsets.top + customEdgeInsets.bottom)
|
|
||||||
presentAnimation?.animationCompletion(hideAnimation: {
|
|
||||||
[weak self] in guard let self = self else { return }
|
|
||||||
self.animation.hideAnimationOfAnimationView(animationView: self, viewSize: size, animated: animated) { _ in
|
|
||||||
return self.presentAnimation!.setCompleteTransitionIsHide(isHide: true)
|
|
||||||
}
|
|
||||||
},endCompletion: {
|
|
||||||
[weak self] in guard let self = self else { return }
|
|
||||||
self.hideCompletions.forEach { hideC in
|
|
||||||
hideC.completion?()
|
|
||||||
hideC.completion = nil
|
|
||||||
self.hideCompletions.removeAll(where: {$0 == hideC})
|
|
||||||
}
|
|
||||||
}, panDimissAnimation: { [weak self] point, frame in
|
|
||||||
guard let self = self else { return (0, 0.4) }
|
|
||||||
return self.animation.panToDimissTransilatePoint(point: point, panViewFrame: frame)
|
|
||||||
}, dimissAnimation: { [weak self] isCancel in guard let self = self else { return }
|
|
||||||
self.toViewContrller?.isDimiss = !isCancel
|
|
||||||
})
|
|
||||||
self.presentAnimation?.duration = animation.animationDuration()
|
|
||||||
}
|
|
||||||
|
|
||||||
public func refreshFrame(animated: Bool = true) {
|
|
||||||
var customSize = customView.systemLayoutSizeFitting(UIView.layoutFittingExpandedSize)
|
|
||||||
if customView.ss_size != .zero {
|
|
||||||
customSize = customView.ss_size
|
|
||||||
}
|
|
||||||
let size = CGSize(width: customSize.width + customEdgeInsets.left + customEdgeInsets.right, height: customSize.height + customEdgeInsets.top + customEdgeInsets.bottom)
|
|
||||||
animation.refreshAnimationOfAnimationView(animationView: self, viewSize: size, animated: animated, completion: nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@objc private func backgroundMaskAction() {
|
|
||||||
if canTouchMaskHide {
|
|
||||||
if fromViewController != nil {
|
|
||||||
dimissView(animated: isTouchMaskHideAnimated ?? false)
|
|
||||||
} else {
|
|
||||||
hideView(animated: isTouchMaskHideAnimated ?? false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func superViewController(view: UIView) -> UIViewController? {
|
|
||||||
var next = view.next
|
|
||||||
while next != nil {
|
|
||||||
if next is UIViewController {
|
|
||||||
return next as? UIViewController
|
|
||||||
} else {
|
|
||||||
next = next!.next
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
public func observeHideCompletion(completion: (() -> Void)?) {
|
|
||||||
let completionData = HideCompletionData()
|
|
||||||
completionData.completion = completion
|
|
||||||
self.hideCompletions.append(completionData)
|
|
||||||
}
|
|
||||||
|
|
||||||
required public init?(coder: NSCoder) {
|
|
||||||
fatalError("init(coder:) has not been implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -1,127 +0,0 @@
|
|||||||
//
|
|
||||||
// SSAlertViewExtention.swift
|
|
||||||
// SSAlert
|
|
||||||
//
|
|
||||||
// Created by yangsq on 2021/8/13.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
extension SSAlertView {
|
|
||||||
@discardableResult
|
|
||||||
public class func alertView(style: SSAlertAction.Style, title: String? = nil, message: String? = nil, cancelButton: String? = nil, otherButtons: [String] = [], onView: UIView, onTrigger:((Int) -> Void)? = nil) -> SSAlertView {
|
|
||||||
var actionArray = [SSAlertAction]()
|
|
||||||
var cancelAction: SSAlertAction? = nil
|
|
||||||
var alertView: SSAlertView?
|
|
||||||
if style == .alert {
|
|
||||||
if let cancelButton = cancelButton, !cancelButton.isEmpty {
|
|
||||||
cancelAction = SSAlertAction(style: style, title: cancelButton, addAction: {
|
|
||||||
if let onTrigger = onTrigger {
|
|
||||||
onTrigger(0)
|
|
||||||
}
|
|
||||||
alertView?.hide()
|
|
||||||
})
|
|
||||||
actionArray.append(cancelAction!)
|
|
||||||
}
|
|
||||||
otherButtons.forEach { title in
|
|
||||||
let action = SSAlertAction(style: style, title: title) {
|
|
||||||
if let onTrigger = onTrigger {
|
|
||||||
let index = otherButtons.firstIndex(of: title)
|
|
||||||
onTrigger(cancelAction != nil ? (index! + 1) : index!)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
actionArray.append(action)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if style == .actionSheet {
|
|
||||||
otherButtons.forEach { title in
|
|
||||||
let action = SSAlertAction(style: style, title: title) {
|
|
||||||
if let onTrigger = onTrigger {
|
|
||||||
let index = otherButtons.firstIndex(of: title)
|
|
||||||
onTrigger(cancelAction != nil ? (index! + 1) : index!)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
actionArray.append(action)
|
|
||||||
}
|
|
||||||
if let cancelButton = cancelButton, !cancelButton.isEmpty {
|
|
||||||
cancelAction = SSAlertAction(style: style, title: cancelButton, addAction: {
|
|
||||||
if let onTrigger = onTrigger {
|
|
||||||
onTrigger(0)
|
|
||||||
}
|
|
||||||
alertView?.hide()
|
|
||||||
})
|
|
||||||
actionArray.append(cancelAction!)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let customView = SSAlertCommonView(title: title, message: message, style: style, actions: actionArray)
|
|
||||||
customView.backgroundColor = .white
|
|
||||||
alertView = SSAlertView(customView: customView, onView: onView, animation: SSAlertDefaultAnmation(state: style == .actionSheet ? .fromBottom : .fromCenter), maskType: .black)
|
|
||||||
alertView!.canTouchMaskHide = false
|
|
||||||
if style == .alert {
|
|
||||||
alertView!.layer.cornerRadius = 10
|
|
||||||
alertView!.layer.masksToBounds = true
|
|
||||||
}
|
|
||||||
return alertView!
|
|
||||||
}
|
|
||||||
|
|
||||||
@discardableResult
|
|
||||||
public class func modalAlertView(style: SSAlertAction.Style, title: String? = nil, message: String? = nil, cancelButton: String? = nil, otherButtons: [String] = [], fromViewController: UIViewController, navigationViewControllerClass: UINavigationController.Type = UINavigationController.self, onTrigger:((Int) -> Void)? = nil) -> SSAlertView {
|
|
||||||
var actionArray = [SSAlertAction]()
|
|
||||||
var cancelAction: SSAlertAction? = nil
|
|
||||||
var alertView: SSAlertView?
|
|
||||||
if style == .alert {
|
|
||||||
if let cancelButton = cancelButton, !cancelButton.isEmpty {
|
|
||||||
cancelAction = SSAlertAction(style: style, title: cancelButton, addAction: {
|
|
||||||
if let onTrigger = onTrigger {
|
|
||||||
onTrigger(0)
|
|
||||||
}
|
|
||||||
alertView?.hide()
|
|
||||||
})
|
|
||||||
actionArray.append(cancelAction!)
|
|
||||||
}
|
|
||||||
otherButtons.forEach { title in
|
|
||||||
let action = SSAlertAction(style: style, title: title) {
|
|
||||||
if let onTrigger = onTrigger {
|
|
||||||
let index = otherButtons.firstIndex(of: title)
|
|
||||||
onTrigger(cancelAction != nil ? (index! + 1) : index!)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
actionArray.append(action)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if style == .actionSheet {
|
|
||||||
otherButtons.forEach { title in
|
|
||||||
let action = SSAlertAction(style: style, title: title) {
|
|
||||||
if let onTrigger = onTrigger {
|
|
||||||
let index = otherButtons.firstIndex(of: title)
|
|
||||||
onTrigger(cancelAction != nil ? (index! + 1) : index!)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
actionArray.append(action)
|
|
||||||
}
|
|
||||||
if let cancelButton = cancelButton, !cancelButton.isEmpty {
|
|
||||||
cancelAction = SSAlertAction(style: style, title: cancelButton, addAction: {
|
|
||||||
if let onTrigger = onTrigger {
|
|
||||||
onTrigger(0)
|
|
||||||
}
|
|
||||||
alertView?.hide()
|
|
||||||
})
|
|
||||||
actionArray.append(cancelAction!)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let customView = SSAlertCommonView(title: title, message: message, style: style, actions: actionArray)
|
|
||||||
customView.backgroundColor = .white
|
|
||||||
alertView = SSAlertView(customView: customView,fromViewController: fromViewController, animation: SSAlertDefaultAnmation(state: style == .actionSheet ? .fromBottom : .fromCenter), navigationControllerClass: navigationViewControllerClass, maskType: .black)
|
|
||||||
alertView!.canTouchMaskHide = false
|
|
||||||
if style == .alert {
|
|
||||||
alertView!.layer.cornerRadius = 10
|
|
||||||
alertView!.layer.masksToBounds = true
|
|
||||||
}
|
|
||||||
return alertView!
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,132 +0,0 @@
|
|||||||
//
|
|
||||||
// UIViewFrameExtension.swift
|
|
||||||
// Pods-SSAlertSwiftDemo
|
|
||||||
//
|
|
||||||
// Created by yangsq on 2021/8/13.
|
|
||||||
//
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
|
|
||||||
public extension UIView {
|
|
||||||
var ss_size: CGSize {
|
|
||||||
get {
|
|
||||||
frame.size
|
|
||||||
}
|
|
||||||
set {
|
|
||||||
var frame = frame
|
|
||||||
frame.size = newValue
|
|
||||||
self.frame = frame
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var ss_origin: CGPoint {
|
|
||||||
get {
|
|
||||||
frame.origin
|
|
||||||
}
|
|
||||||
set {
|
|
||||||
var frame = frame
|
|
||||||
frame.origin = newValue
|
|
||||||
self.frame = frame
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var ss_center: CGPoint {
|
|
||||||
get {
|
|
||||||
center
|
|
||||||
}
|
|
||||||
set {
|
|
||||||
center = newValue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var ss_x: CGFloat {
|
|
||||||
get {
|
|
||||||
frame.origin.x
|
|
||||||
}
|
|
||||||
set {
|
|
||||||
var frame = frame
|
|
||||||
frame.origin.x = newValue
|
|
||||||
self.frame = frame
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var ss_maxX: CGFloat {
|
|
||||||
get {
|
|
||||||
frame.maxX
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var ss_y: CGFloat {
|
|
||||||
get {
|
|
||||||
frame.origin.y
|
|
||||||
}
|
|
||||||
set {
|
|
||||||
var frame = frame
|
|
||||||
frame.origin.y = newValue
|
|
||||||
self.frame = frame
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var ss_maxY: CGFloat {
|
|
||||||
get {
|
|
||||||
frame.maxY
|
|
||||||
}
|
|
||||||
set {
|
|
||||||
var frame = frame
|
|
||||||
frame.origin.y = newValue
|
|
||||||
self.frame = frame
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var ss_w: CGFloat {
|
|
||||||
get {
|
|
||||||
frame.width
|
|
||||||
}
|
|
||||||
set {
|
|
||||||
var frame = frame
|
|
||||||
frame.size.width = newValue
|
|
||||||
self.frame = frame
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var ss_h: CGFloat {
|
|
||||||
get {
|
|
||||||
frame.height
|
|
||||||
}
|
|
||||||
set {
|
|
||||||
var frame = frame
|
|
||||||
frame.size.height = newValue
|
|
||||||
self.frame = frame
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var ss_centerX: CGFloat {
|
|
||||||
get {
|
|
||||||
center.x
|
|
||||||
}
|
|
||||||
set {
|
|
||||||
var center = center
|
|
||||||
center.x = newValue
|
|
||||||
self.center = center
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var ss_centerY: CGFloat {
|
|
||||||
get {
|
|
||||||
center.y
|
|
||||||
}
|
|
||||||
set {
|
|
||||||
var center = center
|
|
||||||
center.y = newValue
|
|
||||||
self.center = center
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func ss_moveToCenter() {
|
|
||||||
if let superV = superview {
|
|
||||||
ss_center = CGPoint(x: superV.frame.width / 2, y: superV.frame.height / 2)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
19
ReadViewDemo/Pods/SnapKit/LICENSE
generated
19
ReadViewDemo/Pods/SnapKit/LICENSE
generated
@ -1,19 +0,0 @@
|
|||||||
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
ReadViewDemo/Pods/SnapKit/README.md
generated
155
ReadViewDemo/Pods/SnapKit/README.md
generated
@ -1,155 +0,0 @@
|
|||||||
<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.
|
|
||||||
|
|
||||||
[](https://travis-ci.org/SnapKit/SnapKit)
|
|
||||||
[](https://github.com/SnapKit/SnapKit)
|
|
||||||
[](https://cocoapods.org/pods/SnapKit)
|
|
||||||
[](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. It’s 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
ReadViewDemo/Pods/SnapKit/Sources/Constraint.swift
generated
341
ReadViewDemo/Pods/SnapKit/Sources/Constraint.swift
generated
@ -1,341 +0,0 @@
|
|||||||
//
|
|
||||||
// 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])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,203 +0,0 @@
|
|||||||
//
|
|
||||||
// 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
|
|
||||||
}
|
|
||||||
@ -1,37 +0,0 @@
|
|||||||
//
|
|
||||||
// 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
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,213 +0,0 @@
|
|||||||
//
|
|
||||||
// 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
ReadViewDemo/Pods/SnapKit/Sources/ConstraintDSL.swift
generated
209
ReadViewDemo/Pods/SnapKit/Sources/ConstraintDSL.swift
generated
@ -1,209 +0,0 @@
|
|||||||
//
|
|
||||||
// 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)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,69 +0,0 @@
|
|||||||
//
|
|
||||||
// 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
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,49 +0,0 @@
|
|||||||
//
|
|
||||||
// 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
|
|
||||||
@ -1,34 +0,0 @@
|
|||||||
//
|
|
||||||
// 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
|
|
||||||
@ -1,72 +0,0 @@
|
|||||||
//
|
|
||||||
// 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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,35 +0,0 @@
|
|||||||
//
|
|
||||||
// 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
|
|
||||||
@ -1,61 +0,0 @@
|
|||||||
//
|
|
||||||
// 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
|
|
||||||
}
|
|
||||||
@ -1,36 +0,0 @@
|
|||||||
//
|
|
||||||
// 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)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,37 +0,0 @@
|
|||||||
//
|
|
||||||
// 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
|
|
||||||
@ -1,66 +0,0 @@
|
|||||||
//
|
|
||||||
// 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
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,36 +0,0 @@
|
|||||||
//
|
|
||||||
// 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
|
|
||||||
@ -1,56 +0,0 @@
|
|||||||
//
|
|
||||||
// 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
ReadViewDemo/Pods/SnapKit/Sources/ConstraintMaker.swift
generated
224
ReadViewDemo/Pods/SnapKit/Sources/ConstraintMaker.swift
generated
@ -1,224 +0,0 @@
|
|||||||
//
|
|
||||||
// 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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,64 +0,0 @@
|
|||||||
//
|
|
||||||
// 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
|
|
||||||
}
|
|
||||||
@ -1,195 +0,0 @@
|
|||||||
//
|
|
||||||
// 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
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,49 +0,0 @@
|
|||||||
//
|
|
||||||
// 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!
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,70 +0,0 @@
|
|||||||
//
|
|
||||||
// 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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,57 +0,0 @@
|
|||||||
//
|
|
||||||
// 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)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,115 +0,0 @@
|
|||||||
//
|
|
||||||
// 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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,75 +0,0 @@
|
|||||||
//
|
|
||||||
// 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
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,69 +0,0 @@
|
|||||||
//
|
|
||||||
// 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
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,77 +0,0 @@
|
|||||||
//
|
|
||||||
// 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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,85 +0,0 @@
|
|||||||
//
|
|
||||||
// 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
|
|
||||||
@ -1,72 +0,0 @@
|
|||||||
//
|
|
||||||
// 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 {
|
|
||||||
}
|
|
||||||
@ -1,48 +0,0 @@
|
|||||||
//
|
|
||||||
// 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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,152 +0,0 @@
|
|||||||
//
|
|
||||||
// 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)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,35 +0,0 @@
|
|||||||
//
|
|
||||||
// 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
|
|
||||||
@ -1,101 +0,0 @@
|
|||||||
//
|
|
||||||
// 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
ReadViewDemo/Pods/SnapKit/Sources/Debugging.swift
generated
169
ReadViewDemo/Pods/SnapKit/Sources/Debugging.swift
generated
@ -1,169 +0,0 @@
|
|||||||
//
|
|
||||||
// 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
|
|
||||||
}
|
|
||||||
@ -1,61 +0,0 @@
|
|||||||
//
|
|
||||||
// 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
|
|
||||||
}
|
|
||||||
@ -1,93 +0,0 @@
|
|||||||
//
|
|
||||||
// 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
|
|
||||||
@ -1,14 +0,0 @@
|
|||||||
<?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
ReadViewDemo/Pods/SnapKit/Sources/Typealiases.swift
generated
42
ReadViewDemo/Pods/SnapKit/Sources/Typealiases.swift
generated
@ -1,42 +0,0 @@
|
|||||||
//
|
|
||||||
// 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
|
|
||||||
|
|
||||||
@ -1,36 +0,0 @@
|
|||||||
//
|
|
||||||
// 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,29 +53,6 @@ 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,35 +76,6 @@ 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
|
||||||
|
|||||||
@ -2,6 +2,4 @@ ${PODS_ROOT}/Target Support Files/Pods-ReadViewDemo/Pods-ReadViewDemo-frameworks
|
|||||||
${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}/RDReaderView/RDReaderView.framework
|
||||||
${BUILT_PRODUCTS_DIR}/SSAlertSwift/SSAlertSwift.framework
|
|
||||||
${BUILT_PRODUCTS_DIR}/SnapKit/SnapKit.framework
|
|
||||||
${BUILT_PRODUCTS_DIR}/ZIPFoundation/ZIPFoundation.framework
|
${BUILT_PRODUCTS_DIR}/ZIPFoundation/ZIPFoundation.framework
|
||||||
@ -1,6 +1,4 @@
|
|||||||
${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}/RDReaderView.framework
|
||||||
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SSAlertSwift.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
|
||||||
@ -2,6 +2,4 @@ ${PODS_ROOT}/Target Support Files/Pods-ReadViewDemo/Pods-ReadViewDemo-frameworks
|
|||||||
${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}/RDReaderView/RDReaderView.framework
|
||||||
${BUILT_PRODUCTS_DIR}/SSAlertSwift/SSAlertSwift.framework
|
|
||||||
${BUILT_PRODUCTS_DIR}/SnapKit/SnapKit.framework
|
|
||||||
${BUILT_PRODUCTS_DIR}/ZIPFoundation/ZIPFoundation.framework
|
${BUILT_PRODUCTS_DIR}/ZIPFoundation/ZIPFoundation.framework
|
||||||
@ -1,6 +1,4 @@
|
|||||||
${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}/RDReaderView.framework
|
||||||
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SSAlertSwift.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
|
||||||
@ -179,16 +179,12 @@ 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}/RDReaderView/RDReaderView.framework"
|
||||||
install_framework "${BUILT_PRODUCTS_DIR}/SSAlertSwift/SSAlertSwift.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}/RDReaderView/RDReaderView.framework"
|
||||||
install_framework "${BUILT_PRODUCTS_DIR}/SSAlertSwift/SSAlertSwift.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
|
||||||
|
|||||||
@ -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}/SSAlertSwift" "${PODS_CONFIGURATION_BUILD_DIR}/SnapKit" "${PODS_CONFIGURATION_BUILD_DIR}/ZIPFoundation"
|
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/DTCoreText" "${PODS_CONFIGURATION_BUILD_DIR}/DTFoundation" "${PODS_CONFIGURATION_BUILD_DIR}/RDReaderView" "${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}/SSAlertSwift/SSAlertSwift.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SnapKit/SnapKit.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}/RDReaderView/RDReaderView.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 $(SDKROOT)/usr/lib/swift
|
LIBRARY_SEARCH_PATHS = $(inherited) "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift
|
||||||
OTHER_LDFLAGS = $(inherited) -l"swiftCoreGraphics" -l"xml2" -framework "CoreGraphics" -framework "CoreText" -framework "DTCoreText" -framework "DTFoundation" -framework "ImageIO" -framework "MediaPlayer" -framework "QuartzCore" -framework "RDReaderView" -framework "SSAlertSwift" -framework "SnapKit" -framework "ZIPFoundation"
|
OTHER_LDFLAGS = $(inherited) -l"xml2" -framework "CoreGraphics" -framework "CoreText" -framework "DTCoreText" -framework "DTFoundation" -framework "ImageIO" -framework "MediaPlayer" -framework "QuartzCore" -framework "RDReaderView" -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}/SSAlertSwift" "-F${PODS_CONFIGURATION_BUILD_DIR}/SnapKit" "-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}/RDReaderView" "-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}/SSAlertSwift" "${PODS_CONFIGURATION_BUILD_DIR}/SnapKit" "${PODS_CONFIGURATION_BUILD_DIR}/ZIPFoundation"
|
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/DTCoreText" "${PODS_CONFIGURATION_BUILD_DIR}/DTFoundation" "${PODS_CONFIGURATION_BUILD_DIR}/RDReaderView" "${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}/SSAlertSwift/SSAlertSwift.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SnapKit/SnapKit.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}/RDReaderView/RDReaderView.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 $(SDKROOT)/usr/lib/swift
|
LIBRARY_SEARCH_PATHS = $(inherited) "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift
|
||||||
OTHER_LDFLAGS = $(inherited) -l"swiftCoreGraphics" -l"xml2" -framework "CoreGraphics" -framework "CoreText" -framework "DTCoreText" -framework "DTFoundation" -framework "ImageIO" -framework "MediaPlayer" -framework "QuartzCore" -framework "RDReaderView" -framework "SSAlertSwift" -framework "SnapKit" -framework "ZIPFoundation"
|
OTHER_LDFLAGS = $(inherited) -l"xml2" -framework "CoreGraphics" -framework "CoreText" -framework "DTCoreText" -framework "DTFoundation" -framework "ImageIO" -framework "MediaPlayer" -framework "QuartzCore" -framework "RDReaderView" -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}/SSAlertSwift" "-F${PODS_CONFIGURATION_BUILD_DIR}/SnapKit" "-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}/RDReaderView" "-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,9 +1,9 @@
|
|||||||
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}/RDReaderView
|
||||||
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/DTCoreText" "${PODS_CONFIGURATION_BUILD_DIR}/DTFoundation" "${PODS_CONFIGURATION_BUILD_DIR}/SSAlertSwift" "${PODS_CONFIGURATION_BUILD_DIR}/SnapKit" "${PODS_CONFIGURATION_BUILD_DIR}/ZIPFoundation"
|
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/DTCoreText" "${PODS_CONFIGURATION_BUILD_DIR}/DTFoundation" "${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 "SSAlertSwift" -framework "SnapKit" -framework "ZIPFoundation"
|
OTHER_LDFLAGS = $(inherited) -framework "CoreGraphics" -framework "CoreText" -framework "DTCoreText" -framework "ImageIO" -framework "MediaPlayer" -framework "QuartzCore" -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)
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
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}/RDReaderView
|
||||||
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/DTCoreText" "${PODS_CONFIGURATION_BUILD_DIR}/DTFoundation" "${PODS_CONFIGURATION_BUILD_DIR}/SSAlertSwift" "${PODS_CONFIGURATION_BUILD_DIR}/SnapKit" "${PODS_CONFIGURATION_BUILD_DIR}/ZIPFoundation"
|
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/DTCoreText" "${PODS_CONFIGURATION_BUILD_DIR}/DTFoundation" "${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 "SSAlertSwift" -framework "SnapKit" -framework "ZIPFoundation"
|
OTHER_LDFLAGS = $(inherited) -framework "CoreGraphics" -framework "CoreText" -framework "DTCoreText" -framework "ImageIO" -framework "MediaPlayer" -framework "QuartzCore" -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)
|
||||||
|
|||||||
@ -1,26 +0,0 @@
|
|||||||
<?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.15</string>
|
|
||||||
<key>CFBundleSignature</key>
|
|
||||||
<string>????</string>
|
|
||||||
<key>CFBundleVersion</key>
|
|
||||||
<string>${CURRENT_PROJECT_VERSION}</string>
|
|
||||||
<key>NSPrincipalClass</key>
|
|
||||||
<string></string>
|
|
||||||
</dict>
|
|
||||||
</plist>
|
|
||||||
@ -1,5 +0,0 @@
|
|||||||
#import <Foundation/Foundation.h>
|
|
||||||
@interface PodsDummy_SSAlertSwift : NSObject
|
|
||||||
@end
|
|
||||||
@implementation PodsDummy_SSAlertSwift
|
|
||||||
@end
|
|
||||||
@ -1,12 +0,0 @@
|
|||||||
#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
|
|
||||||
|
|
||||||
@ -1,16 +0,0 @@
|
|||||||
#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 SSAlertSwiftVersionNumber;
|
|
||||||
FOUNDATION_EXPORT const unsigned char SSAlertSwiftVersionString[];
|
|
||||||
|
|
||||||
@ -1,14 +0,0 @@
|
|||||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
|
|
||||||
CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/SSAlertSwift
|
|
||||||
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
|
|
||||||
LIBRARY_SEARCH_PATHS = $(inherited) "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift
|
|
||||||
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}/SSAlertSwift
|
|
||||||
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,6 +0,0 @@
|
|||||||
framework module SSAlertSwift {
|
|
||||||
umbrella header "SSAlertSwift-umbrella.h"
|
|
||||||
|
|
||||||
export *
|
|
||||||
module * { export * }
|
|
||||||
}
|
|
||||||
@ -1,14 +0,0 @@
|
|||||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
|
|
||||||
CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/SSAlertSwift
|
|
||||||
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
|
|
||||||
LIBRARY_SEARCH_PATHS = $(inherited) "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift
|
|
||||||
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}/SSAlertSwift
|
|
||||||
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,24 +0,0 @@
|
|||||||
<?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>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>BNDL</string>
|
|
||||||
<key>CFBundleShortVersionString</key>
|
|
||||||
<string>5.7.1</string>
|
|
||||||
<key>CFBundleSignature</key>
|
|
||||||
<string>????</string>
|
|
||||||
<key>CFBundleVersion</key>
|
|
||||||
<string>1</string>
|
|
||||||
<key>NSPrincipalClass</key>
|
|
||||||
<string></string>
|
|
||||||
</dict>
|
|
||||||
</plist>
|
|
||||||
@ -1,26 +0,0 @@
|
|||||||
<?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>5.7.1</string>
|
|
||||||
<key>CFBundleSignature</key>
|
|
||||||
<string>????</string>
|
|
||||||
<key>CFBundleVersion</key>
|
|
||||||
<string>${CURRENT_PROJECT_VERSION}</string>
|
|
||||||
<key>NSPrincipalClass</key>
|
|
||||||
<string></string>
|
|
||||||
</dict>
|
|
||||||
</plist>
|
|
||||||
@ -1,5 +0,0 @@
|
|||||||
#import <Foundation/Foundation.h>
|
|
||||||
@interface PodsDummy_SnapKit : NSObject
|
|
||||||
@end
|
|
||||||
@implementation PodsDummy_SnapKit
|
|
||||||
@end
|
|
||||||
@ -1,12 +0,0 @@
|
|||||||
#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
|
|
||||||
|
|
||||||
@ -1,16 +0,0 @@
|
|||||||
#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 SnapKitVersionNumber;
|
|
||||||
FOUNDATION_EXPORT const unsigned char SnapKitVersionString[];
|
|
||||||
|
|
||||||
@ -1,15 +0,0 @@
|
|||||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
|
|
||||||
CONFIGURATION_BUILD_DIR = ${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 $(SDKROOT)/usr/lib/swift
|
|
||||||
OTHER_LDFLAGS = $(inherited) -l"swiftCoreGraphics"
|
|
||||||
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}/SnapKit
|
|
||||||
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,6 +0,0 @@
|
|||||||
framework module SnapKit {
|
|
||||||
umbrella header "SnapKit-umbrella.h"
|
|
||||||
|
|
||||||
export *
|
|
||||||
module * { export * }
|
|
||||||
}
|
|
||||||
@ -1,15 +0,0 @@
|
|||||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
|
|
||||||
CONFIGURATION_BUILD_DIR = ${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 $(SDKROOT)/usr/lib/swift
|
|
||||||
OTHER_LDFLAGS = $(inherited) -l"swiftCoreGraphics"
|
|
||||||
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}/SnapKit
|
|
||||||
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
|
|
||||||
@ -166,10 +166,23 @@ enum RDEPUBJavaScriptBridge {
|
|||||||
return string
|
return string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Encodes a string value as a safe JavaScript string literal.
|
||||||
|
///
|
||||||
|
/// Uses JSON serialization to handle escaping of quotes, backslashes, control
|
||||||
|
/// characters, and Unicode, then strips the array wrapper to produce a standalone
|
||||||
|
/// JS string literal (including surrounding quotes).
|
||||||
|
///
|
||||||
|
/// - Parameter value: The string to encode, or `nil` which produces the JS keyword `null`.
|
||||||
|
/// - Returns: A JavaScript string literal safe for inline embedding in JS source.
|
||||||
private static func javaScriptStringLiteral(_ value: String?) -> String {
|
private static func javaScriptStringLiteral(_ value: String?) -> String {
|
||||||
guard let value else { return "null" }
|
guard let value else { return "null" }
|
||||||
return jsonString(from: [value], fallback: "[null]")
|
guard JSONSerialization.isValidJSONObject([value]),
|
||||||
.replacingOccurrences(of: "[", with: "")
|
let data = try? JSONSerialization.data(withJSONObject: [value], options: []),
|
||||||
.replacingOccurrences(of: "]", with: "")
|
let arrayString = String(data: data, encoding: .utf8) else {
|
||||||
|
return "null"
|
||||||
|
}
|
||||||
|
// JSON encodes ["value"] as `["value"]`. Drop the leading `[` and trailing `]`
|
||||||
|
// to yield `"value"` — a valid JS string literal with all special characters escaped.
|
||||||
|
return String(arrayString.dropFirst().dropLast())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -37,12 +37,20 @@ public struct RDEPUBNavigatorLayoutContext: Equatable {
|
|||||||
return CGSize(width: width, height: containerSize.height)
|
return CGSize(width: width, height: containerSize.height)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Content insets that account for safe area (Dynamic Island / notch) on iPhone.
|
||||||
|
/// Uses the larger of safeAreaInsets and reflowableContentInsets for top/bottom
|
||||||
|
/// to ensure content is never hidden under the Dynamic Island or home indicator.
|
||||||
|
public var safeReflowableContentInsets: UIEdgeInsets {
|
||||||
|
let top = max(safeAreaInsets.top, reflowableContentInsets.top)
|
||||||
|
let bottom = max(safeAreaInsets.bottom, reflowableContentInsets.bottom)
|
||||||
|
let left = max(safeAreaInsets.left, reflowableContentInsets.left)
|
||||||
|
let right = max(safeAreaInsets.right, reflowableContentInsets.right)
|
||||||
|
return UIEdgeInsets(top: top, left: left, bottom: bottom, right: right)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fixed layout content insets that respect safe areas on all devices including iPhone.
|
||||||
public var fixedContentInset: UIEdgeInsets {
|
public var fixedContentInset: UIEdgeInsets {
|
||||||
var insets = safeAreaInsets
|
var insets = safeAreaInsets
|
||||||
if userInterfaceIdiom != .phone {
|
|
||||||
insets = .zero
|
|
||||||
}
|
|
||||||
|
|
||||||
let horizontalInsets = max(insets.left, insets.right)
|
let horizontalInsets = max(insets.left, insets.right)
|
||||||
insets.left = horizontalInsets
|
insets.left = horizontalInsets
|
||||||
insets.right = horizontalInsets
|
insets.right = horizontalInsets
|
||||||
|
|||||||
@ -1,21 +0,0 @@
|
|||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
public enum RDEPUBNavigatorState: String, Codable {
|
|
||||||
|
|
||||||
case initializing
|
|
||||||
|
|
||||||
case loading
|
|
||||||
|
|
||||||
case idle
|
|
||||||
|
|
||||||
case jumping
|
|
||||||
|
|
||||||
case moving
|
|
||||||
|
|
||||||
case repaginating
|
|
||||||
|
|
||||||
public var isStableForSnapshotApplication: Bool {
|
|
||||||
self == .idle
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -29,7 +29,11 @@ extension RDEPUBParser {
|
|||||||
let extractionURL = temporaryExtractionDirectory(for: epubURL)
|
let extractionURL = temporaryExtractionDirectory(for: epubURL)
|
||||||
|
|
||||||
if fileManager.fileExists(atPath: extractionURL.path) {
|
if fileManager.fileExists(atPath: extractionURL.path) {
|
||||||
return extractionURL
|
let containerURL = extractionURL.appendingPathComponent("META-INF/container.xml")
|
||||||
|
if fileManager.fileExists(atPath: containerURL.path) {
|
||||||
|
return extractionURL
|
||||||
|
}
|
||||||
|
try? fileManager.removeItem(at: extractionURL)
|
||||||
}
|
}
|
||||||
|
|
||||||
guard let archive = Archive(url: epubURL, accessMode: .read) else {
|
guard let archive = Archive(url: epubURL, accessMode: .read) else {
|
||||||
@ -38,24 +42,50 @@ extension RDEPUBParser {
|
|||||||
|
|
||||||
try fileManager.createDirectory(at: extractionURL, withIntermediateDirectories: true)
|
try fileManager.createDirectory(at: extractionURL, withIntermediateDirectories: true)
|
||||||
|
|
||||||
for entry in archive {
|
do {
|
||||||
guard let destinationURL = validatedExtractionDestination(for: entry.path, extractionRoot: extractionURL) else {
|
for entry in archive {
|
||||||
throw RDEPUBParserError.invalidArchiveEntryPath(entry.path)
|
if shouldSkipEntry(entry.path) { continue }
|
||||||
}
|
|
||||||
switch entry.type {
|
guard let destinationURL = validatedExtractionDestination(for: entry.path, extractionRoot: extractionURL) else {
|
||||||
case .directory:
|
if isCriticalEntry(entry.path) {
|
||||||
try fileManager.createDirectory(at: destinationURL, withIntermediateDirectories: true)
|
throw RDEPUBParserError.invalidArchiveEntryPath(entry.path)
|
||||||
case .file:
|
}
|
||||||
try fileManager.createDirectory(at: destinationURL.deletingLastPathComponent(), withIntermediateDirectories: true)
|
continue
|
||||||
_ = try archive.extract(entry, to: destinationURL)
|
}
|
||||||
case .symlink:
|
switch entry.type {
|
||||||
continue
|
case .directory:
|
||||||
|
try fileManager.createDirectory(at: destinationURL, withIntermediateDirectories: true)
|
||||||
|
case .file:
|
||||||
|
try fileManager.createDirectory(at: destinationURL.deletingLastPathComponent(), withIntermediateDirectories: true)
|
||||||
|
_ = try archive.extract(entry, to: destinationURL)
|
||||||
|
case .symlink:
|
||||||
|
continue
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} catch {
|
||||||
|
try? fileManager.removeItem(at: extractionURL)
|
||||||
|
throw error
|
||||||
}
|
}
|
||||||
|
|
||||||
return extractionURL
|
return extractionURL
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func isCriticalEntry(_ entryPath: String) -> Bool {
|
||||||
|
let lowercased = entryPath.lowercased()
|
||||||
|
return lowercased == "mimetype"
|
||||||
|
|| lowercased.hasPrefix("meta-inf/")
|
||||||
|
|| lowercased.hasSuffix(".opf")
|
||||||
|
}
|
||||||
|
|
||||||
|
private func shouldSkipEntry(_ entryPath: String) -> Bool {
|
||||||
|
let lowercased = entryPath.lowercased()
|
||||||
|
if lowercased.hasPrefix("__macosx/") { return true }
|
||||||
|
if lowercased.hasPrefix(".ds_store") { return true }
|
||||||
|
if lowercased.contains("/.ds_store") { return true }
|
||||||
|
if lowercased.hasSuffix("/thumbs.db") { return true }
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
private func validatedExtractionDestination(for entryPath: String, extractionRoot: URL) -> URL? {
|
private func validatedExtractionDestination(for entryPath: String, extractionRoot: URL) -> URL? {
|
||||||
|
|
||||||
if entryPath.hasPrefix("/") {
|
if entryPath.hasPrefix("/") {
|
||||||
|
|||||||
@ -21,6 +21,64 @@ public final class RDEPUBParser {
|
|||||||
|
|
||||||
public init() {}
|
public init() {}
|
||||||
|
|
||||||
|
// MARK: - Cache Management
|
||||||
|
|
||||||
|
/// The base directory used for EPUB extraction caches.
|
||||||
|
public static var extractionCacheBaseDirectory: URL {
|
||||||
|
FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first?
|
||||||
|
.appendingPathComponent("ssreaderview-epub", isDirectory: true)
|
||||||
|
?? FileManager.default.temporaryDirectory.appendingPathComponent("ssreaderview-epub", isDirectory: true)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Removes all EPUB extraction caches from disk.
|
||||||
|
/// Call this when the host app wants to free disk space, e.g. on didReceiveMemoryWarning
|
||||||
|
/// or during cleanup when the reader is no longer needed.
|
||||||
|
public static func cleanExtractionCache() throws {
|
||||||
|
let fileManager = FileManager.default
|
||||||
|
let baseURL = extractionCacheBaseDirectory
|
||||||
|
guard fileManager.fileExists(atPath: baseURL.path) else { return }
|
||||||
|
try fileManager.removeItem(at: baseURL)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Evicts extraction caches until total disk usage is below the given threshold,
|
||||||
|
/// removing least-recently-accessed directories first.
|
||||||
|
/// - Parameter maxBytes: Maximum total bytes for all extraction caches. Defaults to 500 MB.
|
||||||
|
public static func evictExtractionCache(maxBytes: UInt64 = 500 * 1024 * 1024) throws {
|
||||||
|
let fileManager = FileManager.default
|
||||||
|
let baseURL = extractionCacheBaseDirectory
|
||||||
|
guard fileManager.fileExists(atPath: baseURL.path) else { return }
|
||||||
|
|
||||||
|
let contents = try fileManager.contentsOfDirectory(
|
||||||
|
at: baseURL,
|
||||||
|
includingPropertiesForKeys: [.contentAccessDateKey, .isDirectoryKey],
|
||||||
|
options: .skipsHiddenFiles
|
||||||
|
)
|
||||||
|
|
||||||
|
var entries: [(url: URL, accessDate: Date, size: UInt64)] = []
|
||||||
|
var totalSize: UInt64 = 0
|
||||||
|
|
||||||
|
for directoryURL in contents {
|
||||||
|
guard let resourceValues = try? directoryURL.resourceValues(forKeys: [.isDirectoryKey, .contentAccessDateKey]),
|
||||||
|
resourceValues.isDirectory == true else {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
let directorySize = directoryURL.directorySize
|
||||||
|
let accessDate = resourceValues.contentAccessDate ?? Date.distantPast
|
||||||
|
entries.append((url: directoryURL, accessDate: accessDate, size: directorySize))
|
||||||
|
totalSize += directorySize
|
||||||
|
}
|
||||||
|
|
||||||
|
guard totalSize > maxBytes else { return }
|
||||||
|
|
||||||
|
entries.sort { $0.accessDate < $1.accessDate }
|
||||||
|
|
||||||
|
for entry in entries {
|
||||||
|
guard totalSize > maxBytes else { break }
|
||||||
|
try? fileManager.removeItem(at: entry.url)
|
||||||
|
totalSize -= entry.size
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public func makePublication() -> RDEPUBPublication {
|
public func makePublication() -> RDEPUBPublication {
|
||||||
RDEPUBPublication(parser: self)
|
RDEPUBPublication(parser: self)
|
||||||
}
|
}
|
||||||
@ -75,4 +133,24 @@ public final class RDEPUBParser {
|
|||||||
public func parseNavDocument(_ navURL: URL, baseURL: URL? = nil) -> [EPUBTableOfContentsItem] {
|
public func parseNavDocument(_ navURL: URL, baseURL: URL? = nil) -> [EPUBTableOfContentsItem] {
|
||||||
parseNavDocumentItems(at: navURL, baseURL: baseURL ?? navURL.deletingLastPathComponent())
|
parseNavDocumentItems(at: navURL, baseURL: baseURL ?? navURL.deletingLastPathComponent())
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - URL Directory Size Extension
|
||||||
|
|
||||||
|
private extension URL {
|
||||||
|
var directorySize: UInt64 {
|
||||||
|
let fileManager = FileManager.default
|
||||||
|
guard let enumerator = fileManager.enumerator(at: self, includingPropertiesForKeys: [.fileSizeKey], options: [.skipsHiddenFiles], errorHandler: nil) else {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
var totalSize: UInt64 = 0
|
||||||
|
for case let fileURL as URL in enumerator {
|
||||||
|
guard let resourceValues = try? fileURL.resourceValues(forKeys: [.fileSizeKey]),
|
||||||
|
let fileSize = resourceValues.fileSize else {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
totalSize += UInt64(fileSize)
|
||||||
|
}
|
||||||
|
return totalSize
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -7,18 +7,10 @@ public final class RDEPUBReadingSession {
|
|||||||
|
|
||||||
public let publication: RDEPUBPublication
|
public let publication: RDEPUBPublication
|
||||||
|
|
||||||
public private(set) var navigatorState: RDEPUBNavigatorState = .initializing
|
|
||||||
|
|
||||||
public private(set) var activePages: [EPUBPage] = []
|
public private(set) var activePages: [EPUBPage] = []
|
||||||
|
|
||||||
public private(set) var activeChapters: [EPUBChapterInfo] = []
|
public private(set) var activeChapters: [EPUBChapterInfo] = []
|
||||||
|
|
||||||
public private(set) var stagedPages: [EPUBPage]?
|
|
||||||
|
|
||||||
public private(set) var stagedChapters: [EPUBChapterInfo]?
|
|
||||||
|
|
||||||
public private(set) var stagedRestoreLocation: RDEPUBLocation?
|
|
||||||
|
|
||||||
public private(set) var pendingNavigationLocation: RDEPUBLocation?
|
public private(set) var pendingNavigationLocation: RDEPUBLocation?
|
||||||
|
|
||||||
public private(set) var pendingNavigationPageNum: Int?
|
public private(set) var pendingNavigationPageNum: Int?
|
||||||
@ -37,16 +29,10 @@ public final class RDEPUBReadingSession {
|
|||||||
publication.resourceResolver
|
publication.resourceResolver
|
||||||
}
|
}
|
||||||
|
|
||||||
public func transition(to state: RDEPUBNavigatorState) {
|
|
||||||
navigatorState = state
|
|
||||||
}
|
|
||||||
|
|
||||||
public func resetRuntimeState() {
|
public func resetRuntimeState() {
|
||||||
navigatorState = .initializing
|
|
||||||
activePages = []
|
activePages = []
|
||||||
activeChapters = []
|
activeChapters = []
|
||||||
clearPendingNavigation()
|
clearPendingNavigation()
|
||||||
clearStagedSnapshot()
|
|
||||||
currentViewport = nil
|
currentViewport = nil
|
||||||
currentReadingContext = nil
|
currentReadingContext = nil
|
||||||
}
|
}
|
||||||
@ -56,36 +42,6 @@ public final class RDEPUBReadingSession {
|
|||||||
activeChapters = snapshot.chapters
|
activeChapters = snapshot.chapters
|
||||||
}
|
}
|
||||||
|
|
||||||
public func stageSnapshot(_ snapshot: PaginationSnapshot, restoreLocation: RDEPUBLocation?) {
|
|
||||||
stagedPages = snapshot.pages
|
|
||||||
stagedChapters = snapshot.chapters
|
|
||||||
stagedRestoreLocation = restoreLocation
|
|
||||||
}
|
|
||||||
|
|
||||||
public func stagedSnapshot() -> PaginationSnapshot? {
|
|
||||||
guard let stagedPages, let stagedChapters else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return (stagedPages, stagedChapters)
|
|
||||||
}
|
|
||||||
|
|
||||||
public func consumeStagedSnapshotIfAllowed() -> (snapshot: PaginationSnapshot, restoreLocation: RDEPUBLocation?)? {
|
|
||||||
guard navigatorState.isStableForSnapshotApplication,
|
|
||||||
let stagedPages,
|
|
||||||
let stagedChapters else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
let restoreLocation = stagedRestoreLocation
|
|
||||||
clearStagedSnapshot()
|
|
||||||
return ((stagedPages, stagedChapters), restoreLocation)
|
|
||||||
}
|
|
||||||
|
|
||||||
public func clearStagedSnapshot() {
|
|
||||||
stagedPages = nil
|
|
||||||
stagedChapters = nil
|
|
||||||
stagedRestoreLocation = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
public func clearPendingNavigation() {
|
public func clearPendingNavigation() {
|
||||||
pendingNavigationLocation = nil
|
pendingNavigationLocation = nil
|
||||||
pendingNavigationPageNum = nil
|
pendingNavigationPageNum = nil
|
||||||
@ -241,7 +197,6 @@ public final class RDEPUBReadingSession {
|
|||||||
pendingNavigationLocation = shouldKeepPendingNavigation ? normalizedLocation : nil
|
pendingNavigationLocation = shouldKeepPendingNavigation ? normalizedLocation : nil
|
||||||
pendingNavigationPageNum = shouldKeepPendingNavigation ? pageIndex + 1 : nil
|
pendingNavigationPageNum = shouldKeepPendingNavigation ? pageIndex + 1 : nil
|
||||||
pendingNavigationHighlightRangeInfo = hasTargetHighlightRangeInfo ? targetHighlightRangeInfo : nil
|
pendingNavigationHighlightRangeInfo = hasTargetHighlightRangeInfo ? targetHighlightRangeInfo : nil
|
||||||
transition(to: .jumping)
|
|
||||||
return pageIndex + 1
|
return pageIndex + 1
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -292,9 +247,6 @@ public final class RDEPUBReadingSession {
|
|||||||
if pendingNavigationPageNum == pageNumber {
|
if pendingNavigationPageNum == pageNumber {
|
||||||
clearPendingNavigation()
|
clearPendingNavigation()
|
||||||
}
|
}
|
||||||
if navigatorState == .jumping || navigatorState == .moving {
|
|
||||||
transition(to: .idle)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public func currentReadingLocation(bookIdentifier: String?) -> RDEPUBLocation? {
|
public func currentReadingLocation(bookIdentifier: String?) -> RDEPUBLocation? {
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import WebKit
|
import WebKit
|
||||||
|
|
||||||
@ -19,6 +18,8 @@ public final class RDEPUBResourceURLSchemeHandler: NSObject, WKURLSchemeHandler
|
|||||||
|
|
||||||
private let syncQueue = DispatchQueue(label: "com.ssreaderview.epub.scheme-handler")
|
private let syncQueue = DispatchQueue(label: "com.ssreaderview.epub.scheme-handler")
|
||||||
|
|
||||||
|
private let ioQueue = DispatchQueue(label: "com.ssreaderview.epub.scheme-handler.io", qos: .utility)
|
||||||
|
|
||||||
private var activeTasks: [ObjectIdentifier: Bool] = [:]
|
private var activeTasks: [ObjectIdentifier: Bool] = [:]
|
||||||
private static let debugMetricsQueue = DispatchQueue(label: "com.ssreaderview.epub.scheme-handler.metrics")
|
private static let debugMetricsQueue = DispatchQueue(label: "com.ssreaderview.epub.scheme-handler.metrics")
|
||||||
private static var streamedResponseCount = 0
|
private static var streamedResponseCount = 0
|
||||||
@ -69,6 +70,8 @@ public final class RDEPUBResourceURLSchemeHandler: NSObject, WKURLSchemeHandler
|
|||||||
expectedContentLength: 0,
|
expectedContentLength: 0,
|
||||||
textEncodingName: Self.textEncodingName(for: requestURL.pathExtension)
|
textEncodingName: Self.textEncodingName(for: requestURL.pathExtension)
|
||||||
)
|
)
|
||||||
|
// H-08 fix: These calls happen synchronously in webView(_:start:) which
|
||||||
|
// runs on the same queue WebKit calls start on, so no thread violation here.
|
||||||
urlSchemeTask.didReceive(response)
|
urlSchemeTask.didReceive(response)
|
||||||
urlSchemeTask.didReceive(Data())
|
urlSchemeTask.didReceive(Data())
|
||||||
urlSchemeTask.didFinish()
|
urlSchemeTask.didFinish()
|
||||||
@ -116,61 +119,153 @@ public final class RDEPUBResourceURLSchemeHandler: NSObject, WKURLSchemeHandler
|
|||||||
return size
|
return size
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// H-08 fix: Dispatch all WKURLSchemeTask completion callbacks to the main queue,
|
||||||
|
/// ensuring they run on the same serial queue that WebKit calls start() on.
|
||||||
|
/// Also: cancelled tasks now call didFailWithError(NSURLErrorCancelled) instead
|
||||||
|
/// of silently returning, satisfying the protocol requirement that every started
|
||||||
|
/// task must receive a completion callback.
|
||||||
private func respondWithInMemoryData(fileURL: URL, requestURL: URL, taskID: ObjectIdentifier, urlSchemeTask: any WKURLSchemeTask) {
|
private func respondWithInMemoryData(fileURL: URL, requestURL: URL, taskID: ObjectIdentifier, urlSchemeTask: any WKURLSchemeTask) {
|
||||||
do {
|
ioQueue.async { [weak self] in
|
||||||
let data = try Data(contentsOf: fileURL)
|
guard let self else {
|
||||||
guard isTaskActive(taskID) else { return }
|
DispatchQueue.main.async {
|
||||||
let response = URLResponse(
|
urlSchemeTask.didFailWithError(NSError(domain: NSURLErrorDomain, code: NSURLErrorCancelled))
|
||||||
url: requestURL,
|
}
|
||||||
mimeType: Self.mimeType(for: fileURL.pathExtension),
|
return
|
||||||
expectedContentLength: data.count,
|
}
|
||||||
textEncodingName: Self.textEncodingName(for: fileURL.pathExtension)
|
do {
|
||||||
)
|
let data = try Data(contentsOf: fileURL)
|
||||||
urlSchemeTask.didReceive(response)
|
guard self.isTaskActive(taskID) else {
|
||||||
urlSchemeTask.didReceive(data)
|
DispatchQueue.main.async {
|
||||||
urlSchemeTask.didFinish()
|
urlSchemeTask.didFailWithError(NSError(domain: NSURLErrorDomain, code: NSURLErrorCancelled))
|
||||||
Self.recordInMemoryResponse()
|
}
|
||||||
RDEPUBWebViewDebug.logSchemeTask("ResourceScheme", requestURL: requestURL, fileURL: fileURL, event: "finished")
|
return
|
||||||
} catch {
|
}
|
||||||
guard isTaskActive(taskID) else { return }
|
let response = URLResponse(
|
||||||
Self.recordFailure()
|
url: requestURL,
|
||||||
RDEPUBWebViewDebug.logSchemeTask("ResourceScheme", requestURL: requestURL, fileURL: fileURL, event: "failed", error: error)
|
mimeType: Self.mimeType(for: fileURL.pathExtension),
|
||||||
urlSchemeTask.didFailWithError(error)
|
expectedContentLength: data.count,
|
||||||
|
textEncodingName: Self.textEncodingName(for: fileURL.pathExtension)
|
||||||
|
)
|
||||||
|
self.dispatchTaskSuccessCallback(
|
||||||
|
taskID: taskID,
|
||||||
|
urlSchemeTask: urlSchemeTask
|
||||||
|
) {
|
||||||
|
urlSchemeTask.didReceive(response)
|
||||||
|
urlSchemeTask.didReceive(data)
|
||||||
|
urlSchemeTask.didFinish()
|
||||||
|
}
|
||||||
|
Self.recordInMemoryResponse()
|
||||||
|
RDEPUBWebViewDebug.logSchemeTask("ResourceScheme", requestURL: requestURL, fileURL: fileURL, event: "finished")
|
||||||
|
} catch {
|
||||||
|
guard self.isTaskActive(taskID) else {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
urlSchemeTask.didFailWithError(NSError(domain: NSURLErrorDomain, code: NSURLErrorCancelled))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
Self.recordFailure()
|
||||||
|
RDEPUBWebViewDebug.logSchemeTask("ResourceScheme", requestURL: requestURL, fileURL: fileURL, event: "failed", error: error)
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
urlSchemeTask.didFailWithError(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.clearTask(taskID)
|
||||||
}
|
}
|
||||||
clearTask(taskID)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private func respondWithStreaming(fileURL: URL, requestURL: URL, taskID: ObjectIdentifier, urlSchemeTask: any WKURLSchemeTask, fileSize: UInt64) {
|
private func respondWithStreaming(fileURL: URL, requestURL: URL, taskID: ObjectIdentifier, urlSchemeTask: any WKURLSchemeTask, fileSize: UInt64) {
|
||||||
let response = URLResponse(
|
ioQueue.async { [weak self] in
|
||||||
url: requestURL,
|
guard let self else {
|
||||||
mimeType: Self.mimeType(for: fileURL.pathExtension),
|
DispatchQueue.main.async {
|
||||||
expectedContentLength: Int(fileSize),
|
urlSchemeTask.didFailWithError(NSError(domain: NSURLErrorDomain, code: NSURLErrorCancelled))
|
||||||
textEncodingName: Self.textEncodingName(for: fileURL.pathExtension)
|
}
|
||||||
)
|
return
|
||||||
urlSchemeTask.didReceive(response)
|
}
|
||||||
|
let response = URLResponse(
|
||||||
|
url: requestURL,
|
||||||
|
mimeType: Self.mimeType(for: fileURL.pathExtension),
|
||||||
|
expectedContentLength: Int(fileSize),
|
||||||
|
textEncodingName: Self.textEncodingName(for: fileURL.pathExtension)
|
||||||
|
)
|
||||||
|
self.dispatchTaskCallbackIfActive(taskID: taskID) {
|
||||||
|
urlSchemeTask.didReceive(response)
|
||||||
|
}
|
||||||
|
|
||||||
guard let fileHandle = try? FileHandle(forReadingFrom: fileURL) else {
|
guard let fileHandle = try? FileHandle(forReadingFrom: fileURL) else {
|
||||||
guard isTaskActive(taskID) else { return }
|
guard self.isTaskActive(taskID) else {
|
||||||
Self.recordFailure()
|
DispatchQueue.main.async {
|
||||||
urlSchemeTask.didFailWithError(NSError(domain: NSURLErrorDomain, code: NSURLErrorFileDoesNotExist))
|
urlSchemeTask.didFailWithError(NSError(domain: NSURLErrorDomain, code: NSURLErrorCancelled))
|
||||||
clearTask(taskID)
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
Self.recordFailure()
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
urlSchemeTask.didFailWithError(NSError(domain: NSURLErrorDomain, code: NSURLErrorFileDoesNotExist))
|
||||||
|
}
|
||||||
|
self.clearTask(taskID)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
let chunkSize = 65_536
|
let chunkSize = 65_536
|
||||||
defer {
|
var streamingCompleted = false
|
||||||
fileHandle.closeFile()
|
defer {
|
||||||
clearTask(taskID)
|
fileHandle.closeFile()
|
||||||
|
if !streamingCompleted {
|
||||||
|
// Task was cancelled during streaming; didFailWithError already sent above
|
||||||
|
// or will be sent by the guard check below. No need to send again.
|
||||||
|
}
|
||||||
|
self.clearTask(taskID)
|
||||||
|
}
|
||||||
|
while true {
|
||||||
|
guard self.isTaskActive(taskID) else {
|
||||||
|
// H-08 fix: Task was cancelled. Send didFailWithError on main queue.
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
urlSchemeTask.didFailWithError(NSError(domain: NSURLErrorDomain, code: NSURLErrorCancelled))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let data = fileHandle.readData(ofLength: chunkSize)
|
||||||
|
if data.isEmpty { break }
|
||||||
|
self.dispatchTaskCallbackIfActive(taskID: taskID) {
|
||||||
|
urlSchemeTask.didReceive(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
streamingCompleted = true
|
||||||
|
self.dispatchTaskSuccessCallback(
|
||||||
|
taskID: taskID,
|
||||||
|
urlSchemeTask: urlSchemeTask
|
||||||
|
) {
|
||||||
|
urlSchemeTask.didFinish()
|
||||||
|
}
|
||||||
|
Self.recordStreamedResponse()
|
||||||
|
RDEPUBWebViewDebug.logSchemeTask("ResourceScheme", requestURL: requestURL, fileURL: fileURL, event: "finished-streaming")
|
||||||
}
|
}
|
||||||
while true {
|
}
|
||||||
guard isTaskActive(taskID) else { return }
|
|
||||||
let data = fileHandle.readData(ofLength: chunkSize)
|
private func dispatchTaskCallbackIfActive(taskID: ObjectIdentifier, _ callback: @escaping () -> Void) {
|
||||||
if data.isEmpty { break }
|
DispatchQueue.main.async { [weak self] in
|
||||||
urlSchemeTask.didReceive(data)
|
guard let self, self.isTaskActive(taskID) else { return }
|
||||||
|
callback()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func dispatchTaskSuccessCallback(
|
||||||
|
taskID: ObjectIdentifier,
|
||||||
|
urlSchemeTask: any WKURLSchemeTask,
|
||||||
|
_ callback: @escaping () -> Void
|
||||||
|
) {
|
||||||
|
DispatchQueue.main.async { [weak self] in
|
||||||
|
guard let self else {
|
||||||
|
urlSchemeTask.didFailWithError(NSError(domain: NSURLErrorDomain, code: NSURLErrorCancelled))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
guard self.isTaskActive(taskID) else {
|
||||||
|
urlSchemeTask.didFailWithError(NSError(domain: NSURLErrorDomain, code: NSURLErrorCancelled))
|
||||||
|
self.clearTask(taskID)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
callback()
|
||||||
}
|
}
|
||||||
urlSchemeTask.didFinish()
|
|
||||||
Self.recordStreamedResponse()
|
|
||||||
RDEPUBWebViewDebug.logSchemeTask("ResourceScheme", requestURL: requestURL, fileURL: fileURL, event: "finished-streaming")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static func recordStreamedResponse() {
|
private static func recordStreamedResponse() {
|
||||||
|
|||||||
@ -36,11 +36,18 @@ extension RDEPUBWebView {
|
|||||||
guard let self else { return }
|
guard let self else { return }
|
||||||
self.handleSelectionMenuAction(action)
|
self.handleSelectionMenuAction(action)
|
||||||
}
|
}
|
||||||
UIMenuController.shared.menuItems = [
|
// M-15: Use UIEditMenuInteraction on iOS 16+ to replace deprecated UIMenuController
|
||||||
UIMenuItem(title: "拷贝", action: #selector(RDEPUBAnnotationWebView.rd_copy(_:))),
|
if #available(iOS 16.0, *) {
|
||||||
UIMenuItem(title: "高亮", action: #selector(RDEPUBAnnotationWebView.rd_highlight(_:))),
|
let interaction = UIEditMenuInteraction(delegate: webView)
|
||||||
UIMenuItem(title: "批注", action: #selector(RDEPUBAnnotationWebView.rd_annotate(_:)))
|
webView.addInteraction(interaction)
|
||||||
]
|
webView._editMenuInteraction = interaction
|
||||||
|
} else {
|
||||||
|
UIMenuController.shared.menuItems = [
|
||||||
|
UIMenuItem(title: "拷贝", action: #selector(RDEPUBAnnotationWebView.rd_copy(_:))),
|
||||||
|
UIMenuItem(title: "高亮", action: #selector(RDEPUBAnnotationWebView.rd_highlight(_:))),
|
||||||
|
UIMenuItem(title: "批注", action: #selector(RDEPUBAnnotationWebView.rd_annotate(_:)))
|
||||||
|
]
|
||||||
|
}
|
||||||
webView.navigationDelegate = self
|
webView.navigationDelegate = self
|
||||||
webView.scrollView.isScrollEnabled = false
|
webView.scrollView.isScrollEnabled = false
|
||||||
webView.scrollView.showsHorizontalScrollIndicator = false
|
webView.scrollView.showsHorizontalScrollIndicator = false
|
||||||
@ -82,6 +89,10 @@ extension RDEPUBWebView {
|
|||||||
if let webView {
|
if let webView {
|
||||||
RDEPUBWebViewDebug.log(debugScope, message: "teardown webView=\(RDEPUBWebViewDebug.webViewID(webView))")
|
RDEPUBWebViewDebug.log(debugScope, message: "teardown webView=\(RDEPUBWebViewDebug.webViewID(webView))")
|
||||||
}
|
}
|
||||||
|
// M-15: Clean up UIEditMenuInteraction on iOS 16+
|
||||||
|
if #available(iOS 16.0, *), let interaction = (webView as? RDEPUBAnnotationWebView)?._editMenuInteraction as? UIEditMenuInteraction {
|
||||||
|
webView?.removeInteraction(interaction)
|
||||||
|
}
|
||||||
webView?.navigationDelegate = nil
|
webView?.navigationDelegate = nil
|
||||||
if let userContentController = webView?.configuration.userContentController {
|
if let userContentController = webView?.configuration.userContentController {
|
||||||
RDEPUBJavaScriptBridge.messageNames.forEach {
|
RDEPUBJavaScriptBridge.messageNames.forEach {
|
||||||
|
|||||||
@ -33,6 +33,10 @@ final class RDEPUBAnnotationWebView: WKWebView {
|
|||||||
|
|
||||||
var onSelectionAction: ((RDEPUBAnnotationMenuAction) -> Void)?
|
var onSelectionAction: ((RDEPUBAnnotationMenuAction) -> Void)?
|
||||||
|
|
||||||
|
// M-15: Backing store for UIEditMenuInteraction (iOS 16+).
|
||||||
|
// Stored as Any? to avoid @available on stored property restriction.
|
||||||
|
var _editMenuInteraction: Any?
|
||||||
|
|
||||||
override var canBecomeFirstResponder: Bool {
|
override var canBecomeFirstResponder: Bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
@ -61,6 +65,29 @@ final class RDEPUBAnnotationWebView: WKWebView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - UIEditMenuInteractionDelegate (iOS 16+)
|
||||||
|
|
||||||
|
@available(iOS 16.0, *)
|
||||||
|
extension RDEPUBAnnotationWebView: UIEditMenuInteractionDelegate {
|
||||||
|
func editMenuInteraction(
|
||||||
|
_ interaction: UIEditMenuInteraction,
|
||||||
|
menuFor configuration: UIEditMenuConfiguration
|
||||||
|
) -> UIMenu {
|
||||||
|
UIMenu(children: [
|
||||||
|
UICommand(title: "拷贝", action: #selector(rd_copy(_:))),
|
||||||
|
UICommand(title: "高亮", action: #selector(rd_highlight(_:))),
|
||||||
|
UICommand(title: "批注", action: #selector(rd_annotate(_:)))
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
func editMenuInteraction(
|
||||||
|
_ interaction: UIEditMenuInteraction,
|
||||||
|
targetRectFor configuration: UIEditMenuConfiguration
|
||||||
|
) -> CGRect {
|
||||||
|
bounds
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public final class RDEPUBWebView: UIView {
|
public final class RDEPUBWebView: UIView {
|
||||||
|
|
||||||
public weak var delegate: RDEPUBWebViewDelegate?
|
public weak var delegate: RDEPUBWebViewDelegate?
|
||||||
@ -126,6 +153,11 @@ public final class RDEPUBWebView: UIView {
|
|||||||
fatalError("init(coder:) has not been implemented")
|
fatalError("init(coder:) has not been implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// M-08: Retain cycle note — webView → configuration.userContentController → self (as WKScriptMessageHandler)
|
||||||
|
// forms a retain cycle. The cycle is broken by calling teardownWebView(), which removes
|
||||||
|
// the message handlers. teardownWebView() is called in reset() and deinit. IMPORTANT:
|
||||||
|
// deinit may never trigger if the cycle is not broken first. Every code path that disposes
|
||||||
|
// of this view MUST call reset() before releasing the last reference.
|
||||||
deinit {
|
deinit {
|
||||||
teardownWebView()
|
teardownWebView()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -104,7 +104,7 @@ final class ChapterPaginationArchive: NSObject, NSSecureCoding {
|
|||||||
|
|
||||||
public final class RDEPUBTextBookCache {
|
public final class RDEPUBTextBookCache {
|
||||||
|
|
||||||
public var schemaVersion: Int = 13
|
public var schemaVersion: Int = 14
|
||||||
|
|
||||||
private let queue = DispatchQueue(label: "com.rdreader.textbookcache", qos: .utility)
|
private let queue = DispatchQueue(label: "com.rdreader.textbookcache", qos: .utility)
|
||||||
|
|
||||||
|
|||||||
@ -27,6 +27,7 @@ struct RDEPUBHTMLNormalizer: RDEPUBTypesettingStage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
cleanedHTML = normalizeAttachmentHTMLMarkers(in: cleanedHTML)
|
cleanedHTML = normalizeAttachmentHTMLMarkers(in: cleanedHTML)
|
||||||
|
cleanedHTML = normalizeBodyLeadingSpacing(in: cleanedHTML)
|
||||||
return cleanedHTML
|
return cleanedHTML
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -129,6 +130,64 @@ struct RDEPUBHTMLNormalizer: RDEPUBTypesettingStage {
|
|||||||
return normalized
|
return normalized
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static func normalizeBodyLeadingSpacing(in html: String) -> String {
|
||||||
|
var normalized = html
|
||||||
|
|
||||||
|
if let bodyStartRegex = try? NSRegularExpression(
|
||||||
|
pattern: #"(<body\b[^>]*>)\s+"#,
|
||||||
|
options: [.caseInsensitive]
|
||||||
|
) {
|
||||||
|
normalized = bodyStartRegex.stringByReplacingMatches(
|
||||||
|
in: normalized,
|
||||||
|
options: [],
|
||||||
|
range: NSRange(location: 0, length: (normalized as NSString).length),
|
||||||
|
withTemplate: "$1"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if let bodyEndRegex = try? NSRegularExpression(
|
||||||
|
pattern: #"\s+(</body>)"#,
|
||||||
|
options: [.caseInsensitive]
|
||||||
|
) {
|
||||||
|
normalized = bodyEndRegex.stringByReplacingMatches(
|
||||||
|
in: normalized,
|
||||||
|
options: [],
|
||||||
|
range: NSRange(location: 0, length: (normalized as NSString).length),
|
||||||
|
withTemplate: "$1"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let firstBlockRegex = try? NSRegularExpression(
|
||||||
|
pattern: #"(<body\b[^>]*>)(\s*)(<(?<tag>h[1-6]|p|div|blockquote|section|article|ul|ol)\b[^>]*>)"#,
|
||||||
|
options: [.caseInsensitive]
|
||||||
|
) else {
|
||||||
|
return normalized
|
||||||
|
}
|
||||||
|
|
||||||
|
let nsNormalized = normalized as NSString
|
||||||
|
let fullRange = NSRange(location: 0, length: nsNormalized.length)
|
||||||
|
guard let match = firstBlockRegex.firstMatch(in: normalized, options: [], range: fullRange),
|
||||||
|
match.numberOfRanges >= 4,
|
||||||
|
let bodyRange = Range(match.range(at: 1), in: normalized),
|
||||||
|
let blockRange = Range(match.range(at: 3), in: normalized) else {
|
||||||
|
return normalized
|
||||||
|
}
|
||||||
|
|
||||||
|
let prefix = String(normalized[..<bodyRange.lowerBound])
|
||||||
|
let bodyTag = String(normalized[bodyRange])
|
||||||
|
let blockTag = String(normalized[blockRange])
|
||||||
|
let normalizedBlockTag = mergeHTMLAttributes(
|
||||||
|
into: blockTag,
|
||||||
|
requiredClass: nil,
|
||||||
|
styleFragments: [
|
||||||
|
"margin-top:0 !important",
|
||||||
|
"-webkit-margin-before:0 !important",
|
||||||
|
"padding-top:0 !important"
|
||||||
|
]
|
||||||
|
)
|
||||||
|
return prefix + bodyTag + normalizedBlockTag + String(normalized[blockRange.upperBound...])
|
||||||
|
}
|
||||||
|
|
||||||
static func replaceMatches(
|
static func replaceMatches(
|
||||||
using regex: NSRegularExpression,
|
using regex: NSRegularExpression,
|
||||||
in source: String,
|
in source: String,
|
||||||
|
|||||||
@ -390,9 +390,6 @@ extension RDEPUBReaderController: RDReaderDataSource, RDReaderPageProvider, RDRe
|
|||||||
if let location = fallbackLocation(for: effectivePageNum) {
|
if let location = fallbackLocation(for: effectivePageNum) {
|
||||||
persist(location: location)
|
persist(location: location)
|
||||||
}
|
}
|
||||||
if readingSession?.navigatorState == .jumping || readingSession?.navigatorState == .moving {
|
|
||||||
readingSession?.transition(to: .idle)
|
|
||||||
}
|
|
||||||
|
|
||||||
runtime.locationCoordinator.recordPageChangeIfNeeded()
|
runtime.locationCoordinator.recordPageChangeIfNeeded()
|
||||||
}
|
}
|
||||||
@ -432,8 +429,9 @@ extension RDEPUBReaderController: RDReaderDataSource, RDReaderPageProvider, RDRe
|
|||||||
|
|
||||||
let currentSize = self.readerView.resolvedSinglePageSize(pageNum: self.readerView.currentPage)
|
let currentSize = self.readerView.resolvedSinglePageSize(pageNum: self.readerView.currentPage)
|
||||||
guard currentSize.width > 0, currentSize.height > 0 else { return }
|
guard currentSize.width > 0, currentSize.height > 0 else { return }
|
||||||
let stillChanged = abs(currentSize.width - self.lastTextPaginationPageSize!.width) > 0.5
|
guard let previousPageSize = self.lastTextPaginationPageSize else { return }
|
||||||
|| abs(currentSize.height - self.lastTextPaginationPageSize!.height) > 0.5
|
let stillChanged = abs(currentSize.width - previousPageSize.width) > 0.5
|
||||||
|
|| abs(currentSize.height - previousPageSize.height) > 0.5
|
||||||
guard stillChanged else { return }
|
guard stillChanged else { return }
|
||||||
self.repaginatePreservingCurrentLocation()
|
self.repaginatePreservingCurrentLocation()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -137,7 +137,6 @@ extension RDEPUBReaderController {
|
|||||||
|
|
||||||
guard let textBook,
|
guard let textBook,
|
||||||
let page = textBook.page(at: pageNumber) else {
|
let page = textBook.page(at: pageNumber) else {
|
||||||
readingSession?.transition(to: .idle)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -6,10 +6,12 @@ public final class RDEPUBReaderController: UIViewController {
|
|||||||
|
|
||||||
public var configuration: RDEPUBReaderConfiguration {
|
public var configuration: RDEPUBReaderConfiguration {
|
||||||
didSet {
|
didSet {
|
||||||
|
// M-09: Apply all configuration side effects even before view is loaded,
|
||||||
|
// but UI-dependent actions only after view is loaded.
|
||||||
readerContext.configuration = configuration
|
readerContext.configuration = configuration
|
||||||
|
guard isViewLoaded else { return }
|
||||||
applyWebViewDebugPolicy()
|
applyWebViewDebugPolicy()
|
||||||
persistReaderSettingsIfNeeded()
|
persistReaderSettingsIfNeeded()
|
||||||
guard isViewLoaded else { return }
|
|
||||||
let oldConfiguration = oldValue
|
let oldConfiguration = oldValue
|
||||||
applyReaderViewConfiguration()
|
applyReaderViewConfiguration()
|
||||||
|
|
||||||
@ -176,11 +178,6 @@ public final class RDEPUBReaderController: UIViewController {
|
|||||||
set { readerContext.paginationToken = newValue }
|
set { readerContext.paginationToken = newValue }
|
||||||
}
|
}
|
||||||
|
|
||||||
var paginator: RDEPUBPaginator? {
|
|
||||||
get { readerContext.paginator }
|
|
||||||
set { readerContext.paginator = newValue }
|
|
||||||
}
|
|
||||||
|
|
||||||
var searchState: RDEPUBSearchState? {
|
var searchState: RDEPUBSearchState? {
|
||||||
get { readerContext.searchState }
|
get { readerContext.searchState }
|
||||||
set { readerContext.searchState = newValue }
|
set { readerContext.searchState = newValue }
|
||||||
@ -291,6 +288,11 @@ public final class RDEPUBReaderController: UIViewController {
|
|||||||
runtime.viewportMonitor.viewDidLayoutSubviews()
|
runtime.viewportMonitor.viewDidLayoutSubviews()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override func didReceiveMemoryWarning() {
|
||||||
|
super.didReceiveMemoryWarning()
|
||||||
|
runtime.handleMemoryWarning()
|
||||||
|
}
|
||||||
|
|
||||||
public override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
|
public override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
|
||||||
super.viewWillTransition(to: size, with: coordinator)
|
super.viewWillTransition(to: size, with: coordinator)
|
||||||
runtime.viewportMonitor.viewWillTransition(with: coordinator)
|
runtime.viewportMonitor.viewWillTransition(with: coordinator)
|
||||||
|
|||||||
@ -77,60 +77,104 @@ public final class RDEPUBUserDefaultsPersistence: RDEPUBReaderPersistence {
|
|||||||
guard let data = defaults.data(forKey: locationPrefix + bookIdentifier) else {
|
guard let data = defaults.data(forKey: locationPrefix + bookIdentifier) else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return try? JSONDecoder().decode(RDEPUBLocation.self, from: data)
|
do {
|
||||||
|
return try JSONDecoder().decode(RDEPUBLocation.self, from: data)
|
||||||
|
} catch {
|
||||||
|
#if DEBUG
|
||||||
|
print("[RDEPUBUserDefaultsPersistence] ⚠️ Failed to decode location for '\(bookIdentifier)': \(error)")
|
||||||
|
#endif
|
||||||
|
return nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func saveLocation(_ location: RDEPUBLocation, for bookIdentifier: String) {
|
public func saveLocation(_ location: RDEPUBLocation, for bookIdentifier: String) {
|
||||||
guard let data = try? JSONEncoder().encode(location) else {
|
do {
|
||||||
return
|
let data = try JSONEncoder().encode(location)
|
||||||
|
defaults.set(data, forKey: locationPrefix + bookIdentifier)
|
||||||
|
} catch {
|
||||||
|
#if DEBUG
|
||||||
|
print("[RDEPUBUserDefaultsPersistence] ⚠️ Failed to encode location for '\(bookIdentifier)': \(error)")
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
defaults.set(data, forKey: locationPrefix + bookIdentifier)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public func loadBookmarks(for bookIdentifier: String) -> [RDEPUBBookmark] {
|
public func loadBookmarks(for bookIdentifier: String) -> [RDEPUBBookmark] {
|
||||||
guard let data = defaults.data(forKey: bookmarksPrefix + bookIdentifier) else {
|
guard let data = defaults.data(forKey: bookmarksPrefix + bookIdentifier) else {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
return (try? JSONDecoder().decode([RDEPUBBookmark].self, from: data)) ?? []
|
do {
|
||||||
|
return try JSONDecoder().decode([RDEPUBBookmark].self, from: data)
|
||||||
|
} catch {
|
||||||
|
#if DEBUG
|
||||||
|
print("[RDEPUBUserDefaultsPersistence] ⚠️ Failed to decode bookmarks for '\(bookIdentifier)': \(error)")
|
||||||
|
#endif
|
||||||
|
return []
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func saveBookmarks(_ bookmarks: [RDEPUBBookmark], for bookIdentifier: String) {
|
public func saveBookmarks(_ bookmarks: [RDEPUBBookmark], for bookIdentifier: String) {
|
||||||
guard let data = try? JSONEncoder().encode(bookmarks) else {
|
do {
|
||||||
return
|
let data = try JSONEncoder().encode(bookmarks)
|
||||||
|
defaults.set(data, forKey: bookmarksPrefix + bookIdentifier)
|
||||||
|
} catch {
|
||||||
|
#if DEBUG
|
||||||
|
print("[RDEPUBUserDefaultsPersistence] ⚠️ Failed to encode bookmarks for '\(bookIdentifier)': \(error)")
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
defaults.set(data, forKey: bookmarksPrefix + bookIdentifier)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public func loadHighlights(for bookIdentifier: String) -> [RDEPUBHighlight] {
|
public func loadHighlights(for bookIdentifier: String) -> [RDEPUBHighlight] {
|
||||||
guard let data = defaults.data(forKey: highlightsPrefix + bookIdentifier) else {
|
guard let data = defaults.data(forKey: highlightsPrefix + bookIdentifier) else {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
return (try? JSONDecoder().decode([RDEPUBHighlight].self, from: data)) ?? []
|
do {
|
||||||
|
return try JSONDecoder().decode([RDEPUBHighlight].self, from: data)
|
||||||
|
} catch {
|
||||||
|
#if DEBUG
|
||||||
|
print("[RDEPUBUserDefaultsPersistence] ⚠️ Failed to decode highlights for '\(bookIdentifier)': \(error)")
|
||||||
|
#endif
|
||||||
|
return []
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func saveHighlights(_ highlights: [RDEPUBHighlight], for bookIdentifier: String) {
|
public func saveHighlights(_ highlights: [RDEPUBHighlight], for bookIdentifier: String) {
|
||||||
guard let data = try? JSONEncoder().encode(highlights) else {
|
do {
|
||||||
return
|
let data = try JSONEncoder().encode(highlights)
|
||||||
}
|
if data.count > 1_048_576 {
|
||||||
if data.count > 1_048_576 {
|
#if DEBUG
|
||||||
|
print("[RDEPUBUserDefaultsPersistence] ⚠️ saveHighlights data size (\(data.count) bytes) exceeds 1MB for: \(bookIdentifier)")
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
defaults.set(data, forKey: highlightsPrefix + bookIdentifier)
|
||||||
|
} catch {
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
print("[RDEPUBUserDefaultsPersistence] ⚠️ saveHighlights data size (\(data.count) bytes) exceeds 1MB for: \(bookIdentifier)")
|
print("[RDEPUBUserDefaultsPersistence] ⚠️ Failed to encode highlights for '\(bookIdentifier)': \(error)")
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
defaults.set(data, forKey: highlightsPrefix + bookIdentifier)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public func loadReaderSettings() -> RDEPUBReaderSettings? {
|
public func loadReaderSettings() -> RDEPUBReaderSettings? {
|
||||||
guard let data = defaults.data(forKey: settingsKey) else {
|
guard let data = defaults.data(forKey: settingsKey) else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return try? JSONDecoder().decode(RDEPUBReaderSettings.self, from: data)
|
do {
|
||||||
|
return try JSONDecoder().decode(RDEPUBReaderSettings.self, from: data)
|
||||||
|
} catch {
|
||||||
|
#if DEBUG
|
||||||
|
print("[RDEPUBUserDefaultsPersistence] ⚠️ Failed to decode reader settings: \(error)")
|
||||||
|
#endif
|
||||||
|
return nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func saveReaderSettings(_ settings: RDEPUBReaderSettings) {
|
public func saveReaderSettings(_ settings: RDEPUBReaderSettings) {
|
||||||
guard let data = try? JSONEncoder().encode(settings) else {
|
do {
|
||||||
return
|
let data = try JSONEncoder().encode(settings)
|
||||||
|
defaults.set(data, forKey: settingsKey)
|
||||||
|
} catch {
|
||||||
|
#if DEBUG
|
||||||
|
print("[RDEPUBUserDefaultsPersistence] ⚠️ Failed to encode reader settings: \(error)")
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
defaults.set(data, forKey: settingsKey)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -154,11 +154,18 @@ public final class RDURLReaderController: UIViewController {
|
|||||||
let bookTitle = bookURL.deletingPathExtension().lastPathComponent
|
let bookTitle = bookURL.deletingPathExtension().lastPathComponent
|
||||||
let pageSize = currentTextPageSize()
|
let pageSize = currentTextPageSize()
|
||||||
let renderStyle = currentTextRenderStyle()
|
let renderStyle = currentTextRenderStyle()
|
||||||
|
let safeInsets = view.safeAreaInsets
|
||||||
|
let edgeInsets = UIEdgeInsets(
|
||||||
|
top: max(epubConfiguration.reflowableContentInsets.top, safeInsets.top),
|
||||||
|
left: max(epubConfiguration.reflowableContentInsets.left, safeInsets.left),
|
||||||
|
bottom: max(epubConfiguration.reflowableContentInsets.bottom, safeInsets.bottom),
|
||||||
|
right: max(epubConfiguration.reflowableContentInsets.right, safeInsets.right)
|
||||||
|
)
|
||||||
let builder = RDPlainTextBookBuilder(
|
let builder = RDPlainTextBookBuilder(
|
||||||
layoutConfig: RDEPUBTextLayoutConfig(
|
layoutConfig: RDEPUBTextLayoutConfig(
|
||||||
frameWidth: pageSize.width,
|
frameWidth: pageSize.width,
|
||||||
frameHeight: pageSize.height,
|
frameHeight: pageSize.height,
|
||||||
edgeInsets: epubConfiguration.reflowableContentInsets,
|
edgeInsets: edgeInsets,
|
||||||
numberOfColumns: 1,
|
numberOfColumns: 1,
|
||||||
columnGap: 20,
|
columnGap: 20,
|
||||||
avoidOrphans: false,
|
avoidOrphans: false,
|
||||||
|
|||||||
@ -3,19 +3,37 @@ import Foundation
|
|||||||
final class RDEPUBChapterDataCache {
|
final class RDEPUBChapterDataCache {
|
||||||
|
|
||||||
private var storage: [Int: RDEPUBRuntimeChapter] = [:]
|
private var storage: [Int: RDEPUBRuntimeChapter] = [:]
|
||||||
|
private var accessOrder: [Int] = [] // H-05: LRU tracking for eviction
|
||||||
|
private let maxEntryCount: Int
|
||||||
|
|
||||||
private let lock = NSLock()
|
private let lock = NSLock()
|
||||||
|
|
||||||
|
init(maxEntryCount: Int = 30) {
|
||||||
|
self.maxEntryCount = maxEntryCount
|
||||||
|
}
|
||||||
|
|
||||||
subscript(_ spineIndex: Int) -> RDEPUBRuntimeChapter? {
|
subscript(_ spineIndex: Int) -> RDEPUBRuntimeChapter? {
|
||||||
get {
|
get {
|
||||||
lock.lock()
|
lock.lock()
|
||||||
defer { lock.unlock() }
|
defer { lock.unlock() }
|
||||||
return storage[spineIndex]
|
guard let chapter = storage[spineIndex] else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
touchLocked(spineIndex)
|
||||||
|
return chapter
|
||||||
}
|
}
|
||||||
set {
|
set {
|
||||||
lock.lock()
|
lock.lock()
|
||||||
defer { lock.unlock() }
|
defer { lock.unlock() }
|
||||||
storage[spineIndex] = newValue
|
if let newValue {
|
||||||
|
storage[spineIndex] = newValue
|
||||||
|
touchLocked(spineIndex)
|
||||||
|
// Evict oldest entries if over limit
|
||||||
|
evictIfNeededLocked()
|
||||||
|
} else {
|
||||||
|
storage.removeValue(forKey: spineIndex)
|
||||||
|
accessOrder.removeAll { $0 == spineIndex }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -29,11 +47,29 @@ final class RDEPUBChapterDataCache {
|
|||||||
lock.lock()
|
lock.lock()
|
||||||
defer { lock.unlock() }
|
defer { lock.unlock() }
|
||||||
storage.removeValue(forKey: spineIndex)
|
storage.removeValue(forKey: spineIndex)
|
||||||
|
accessOrder.removeAll { $0 == spineIndex }
|
||||||
}
|
}
|
||||||
|
|
||||||
func removeAll() {
|
func removeAll() {
|
||||||
lock.lock()
|
lock.lock()
|
||||||
defer { lock.unlock() }
|
defer { lock.unlock() }
|
||||||
storage.removeAll()
|
storage.removeAll()
|
||||||
|
accessOrder.removeAll()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
/// H-05: Evict least recently used entries when cache exceeds maxEntryCount.
|
||||||
|
/// Must be called while holding lock.
|
||||||
|
private func evictIfNeededLocked() {
|
||||||
|
while storage.count > maxEntryCount, let oldest = accessOrder.first {
|
||||||
|
storage.removeValue(forKey: oldest)
|
||||||
|
accessOrder.removeFirst()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Marks an entry as most recently used.
|
||||||
|
/// Must be called while holding lock.
|
||||||
|
private func touchLocked(_ spineIndex: Int) {
|
||||||
|
accessOrder.removeAll { $0 == spineIndex }
|
||||||
|
accessOrder.append(spineIndex)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user